[
  {
    "path": ".gitignore",
    "content": "xcuserdata/\n"
  },
  {
    "path": "MiniSuperApp/AppDelegate/AppComponent.swift",
    "content": "import Foundation\nimport ModernRIBs\n\nfinal class AppComponent: Component<EmptyDependency>, AppRootDependency {\n  \n  init() {\n    super.init(dependency: EmptyComponent())\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/AppDelegate/AppDelegate.swift",
    "content": "import UIKit\nimport ModernRIBs\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  \n  var window: UIWindow?\n  \n  private var launchRouter: LaunchRouting?\n  private var urlHandler: URLHandler?\n  \n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    \n    let window = UIWindow(frame: UIScreen.main.bounds)\n    self.window = window\n    \n    let result = AppRootBuilder(dependency: AppComponent()).build()\n    self.launchRouter = result.launchRouter\n    self.urlHandler = result.urlHandler\n    launchRouter?.launch(from: window)\n    \n    return true\n  }\n  \n}\n\nprotocol URLHandler: AnyObject {\n  func handle(_ url: URL)\n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/AppHomeBuilder.swift",
    "content": "import ModernRIBs\n\npublic protocol AppHomeDependency: Dependency {\n}\n\nfinal class AppHomeComponent: Component<AppHomeDependency>, TransportHomeDependency {\n}\n\n// MARK: - Builder\n\npublic protocol AppHomeBuildable: Buildable {\n  func build(withListener listener: AppHomeListener) -> ViewableRouting\n}\n\npublic final class AppHomeBuilder: Builder<AppHomeDependency>, AppHomeBuildable {\n  \n  public override init(dependency: AppHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: AppHomeListener) -> ViewableRouting {\n    let component = AppHomeComponent(dependency: dependency)\n    let viewController = AppHomeViewController()\n    let interactor = AppHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    \n    let transportHomeBuilder = TransportHomeBuilder(dependency: component)\n    \n    return AppHomeRouter(\n      interactor: interactor,\n      viewController: viewController,\n      transportHomeBuildable: transportHomeBuilder\n    )\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/AppHomeInteractor.swift",
    "content": "import ModernRIBs\n\nprotocol AppHomeRouting: ViewableRouting {\n  func attachTransportHome()\n  func detachTransportHome()\n}\n\nprotocol AppHomePresentable: Presentable {\n  var listener: AppHomePresentableListener? { get set }\n  \n  func updateWidget(_ viewModels: [HomeWidgetViewModel])\n}\n\npublic protocol AppHomeListener: AnyObject {\n  // TODO: Declare methods the interactor can invoke to communicate with other RIBs.\n}\n\nfinal class AppHomeInteractor: PresentableInteractor<AppHomePresentable>, AppHomeInteractable, AppHomePresentableListener {\n  \n  weak var router: AppHomeRouting?\n  weak var listener: AppHomeListener?\n  \n  override init(presenter: AppHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    let viewModels = [\n      HomeWidgetModel(\n        imageName: \"car\",\n        title: \"슈퍼택시\",\n        tapHandler: { [weak self] in\n          self?.router?.attachTransportHome()\n        }\n      ),\n      HomeWidgetModel(\n        imageName: \"cart\",\n        title: \"슈퍼마트\",\n        tapHandler: { }\n      )\n    ]\n    \n    presenter.updateWidget(viewModels.map(HomeWidgetViewModel.init))\n  }\n  \n  func transportHomeDidTapClose() {\n    router?.detachTransportHome()\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/AppHomeRouter.swift",
    "content": "import ModernRIBs\n\n\nprotocol AppHomeInteractable: Interactable, TransportHomeListener {\n  var router: AppHomeRouting? { get set }\n  var listener: AppHomeListener? { get set }\n}\n\nprotocol AppHomeViewControllable: ViewControllable {\n\n}\n\nfinal class AppHomeRouter: ViewableRouter<AppHomeInteractable, AppHomeViewControllable>, AppHomeRouting {\n  \n  private let transportHomeBuildable: TransportHomeBuildable\n  private var transportHomeRouting: Routing?\n  private let transitioningDelegate: PushModalPresentationController\n  \n  init(\n    interactor: AppHomeInteractable,\n    viewController: AppHomeViewControllable,\n    transportHomeBuildable: TransportHomeBuildable\n  ) {\n    self.transitioningDelegate = PushModalPresentationController()\n    self.transportHomeBuildable = transportHomeBuildable\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachTransportHome() {\n    if transportHomeRouting != nil {\n      return\n    }\n    \n    let router = transportHomeBuildable.build(withListener: interactor)\n    presentWithPushTransition(router.viewControllable, animated: true)\n    attachChild(router)\n    self.transportHomeRouting = router\n  }\n  \n  func detachTransportHome() {\n    guard let router = transportHomeRouting else {\n      return\n    }\n    \n    viewController.dismiss(completion: nil)\n    self.transportHomeRouting = nil\n    detachChild(router)\n  }\n  \n  private func presentWithPushTransition(_ viewControllable: ViewControllable, animated: Bool) {\n    viewControllable.uiviewController.modalPresentationStyle = .custom\n    viewControllable.uiviewController.transitioningDelegate = transitioningDelegate\n    viewController.present(viewControllable, animated: true, completion: nil)\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/AppHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol AppHomePresentableListener: AnyObject {\n}\n\nfinal class AppHomeViewController: UIViewController, AppHomePresentable, AppHomeViewControllable {\n  \n  weak var listener: AppHomePresentableListener?\n  \n  private let widgetStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.axis = .horizontal\n    stackView.distribution = .fillEqually\n    stackView.alignment = .top\n    stackView.spacing = 20\n    return stackView\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  func updateWidget(_ viewModels: [HomeWidgetViewModel]) {\n    let views = viewModels.map { HomeWidgetView(viewModel: $0) }\n    \n    views.forEach {\n      $0.addShadowWithRoundedCorners(12)\n      widgetStackView.addArrangedSubview($0)\n    }\n  }\n  \n  private func setupViews() {\n    title = \"홈\"\n    tabBarItem = UITabBarItem(title: \"홈\", image: UIImage(systemName: \"house\"), selectedImage: UIImage(systemName: \"house.fill\"))\n    view.backgroundColor = .backgroundColor\n    view.addSubview(widgetStackView)\n    \n    NSLayoutConstraint.activate([\n      widgetStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),\n      widgetStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      widgetStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)\n    ])\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/HomeWidgetModel.swift",
    "content": "import Foundation\n\nstruct HomeWidgetModel {\n  let imageName: String\n  let title: String\n  let tapHandler: () -> Void\n}\n"
  },
  {
    "path": "MiniSuperApp/AppHome/Views/HomeWidgetView.swift",
    "content": "import UIKit\n\nstruct HomeWidgetViewModel {\n  let image: UIImage?\n  let title: String\n  let tapHandler: () -> Void\n  \n  init(_ model: HomeWidgetModel) {\n    image = UIImage(systemName: model.imageName)\n    title = model.title\n    tapHandler = model.tapHandler\n  }\n}\n\nfinal class HomeWidgetView: UIView {\n  \n  init(viewModel: HomeWidgetViewModel) {\n    super.init(frame: .zero)\n    \n    setupViews()\n    update(with: viewModel)\n  }\n  \n  required init?(coder: NSCoder) {\n    fatalError()\n  }\n  \n  private var tapHandler: (() -> Void)?\n  \n  private func update(with viewModel: HomeWidgetViewModel) {\n    imageView.image = viewModel.image\n    titleLabel.text = viewModel.title\n    tapHandler = viewModel.tapHandler\n  }\n\n  private let imageView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.tintColor = .black\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    return imageView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.textAlignment = .center\n    label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)\n    return label\n  }()\n  \n  private func setupViews() {\n    addSubview(imageView)\n    addSubview(titleLabel)\n    backgroundColor = .white\n    \n    let tap = UITapGestureRecognizer(target: self, action: #selector(didTap))\n    addGestureRecognizer(tap)\n    \n    NSLayoutConstraint.activate([\n      imageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 15),\n      imageView.centerXAnchor.constraint(equalTo: self.centerXAnchor),\n      imageView.widthAnchor.constraint(equalToConstant: 50),\n      imageView.heightAnchor.constraint(equalToConstant: 50),\n      \n      titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5),\n      titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor),\n      titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -14)\n    ])\n  }\n  \n  @objc\n  private func didTap() {\n    tapHandler?()\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/AppRoot/AppRootBuilder.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol AppRootDependency: Dependency {\n  // TODO: Declare the set of dependencies required by this RIB, but cannot be\n  // created by this RIB.\n}\n\nfinal class AppRootComponent: Component<AppRootDependency>, AppHomeDependency, FinanceHomeDependency, ProfileHomeDependency  {\n  \n  // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.\n}\n\n// MARK: - Builder\n\nprotocol AppRootBuildable: Buildable {\n  func build() -> (launchRouter: LaunchRouting, urlHandler: URLHandler)\n}\n\nfinal class AppRootBuilder: Builder<AppRootDependency>, AppRootBuildable {\n  \n  override init(dependency: AppRootDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build() -> (launchRouter: LaunchRouting, urlHandler: URLHandler) {\n    let component = AppRootComponent(dependency: dependency)\n    \n    let tabBar = RootTabBarController()\n    \n    let interactor = AppRootInteractor(presenter: tabBar)\n    \n    let appHome = AppHomeBuilder(dependency: component)\n    let financeHome = FinanceHomeBuilder(dependency: component)\n    let profileHome = ProfileHomeBuilder(dependency: component)\n    let router = AppRootRouter(\n      interactor: interactor,\n      viewController: tabBar,\n      appHome: appHome,\n      financeHome: financeHome,\n      profileHome: profileHome\n    )\n    \n    return (router, interactor)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/AppRoot/AppRootInteractor.swift",
    "content": "import Foundation\nimport ModernRIBs\n\nprotocol AppRootRouting: ViewableRouting {\n  func attachTabs()\n}\n\nprotocol AppRootPresentable: Presentable {\n  var listener: AppRootPresentableListener? { get set }\n  // TODO: Declare methods the interactor can invoke the presenter to present data.\n}\n\nprotocol AppRootListener: AnyObject {\n  // TODO: Declare methods the interactor can invoke to communicate with other RIBs.\n}\n\nfinal class AppRootInteractor: PresentableInteractor<AppRootPresentable>, AppRootInteractable, AppRootPresentableListener, URLHandler {\n  \n  weak var router: AppRootRouting?\n  weak var listener: AppRootListener?\n  \n  // TODO: Add additional dependencies to constructor. Do not perform any logic\n  // in constructor.\n  override init(presenter: AppRootPresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    router?.attachTabs()\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n  \n  func handle(_ url: URL) {\n    \n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/AppRoot/AppRootRouter.swift",
    "content": "import ModernRIBs\n\nprotocol AppRootInteractable: Interactable,\n                              AppHomeListener,\n                              FinanceHomeListener,\n                              ProfileHomeListener {\n  var router: AppRootRouting? { get set }\n  var listener: AppRootListener? { get set }\n}\n\nprotocol AppRootViewControllable: ViewControllable {\n  func setViewControllers(_ viewControllers: [ViewControllable])\n}\n\nfinal class AppRootRouter: LaunchRouter<AppRootInteractable, AppRootViewControllable>, AppRootRouting {\n  \n  private let appHome: AppHomeBuildable\n  private let financeHome: FinanceHomeBuildable\n  private let profileHome: ProfileHomeBuildable\n  \n  private var appHomeRouting: ViewableRouting?\n  private var financeHomeRouting: ViewableRouting?\n  private var profileHomeRouting: ViewableRouting?\n  \n  init(\n    interactor: AppRootInteractable,\n    viewController: AppRootViewControllable,\n    appHome: AppHomeBuildable,\n    financeHome: FinanceHomeBuildable,\n    profileHome: ProfileHomeBuildable\n  ) {\n    self.appHome = appHome\n    self.financeHome = financeHome\n    self.profileHome = profileHome\n    \n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachTabs() {\n    let appHomeRouting = appHome.build(withListener: interactor)\n    let financeHomeRouting = financeHome.build(withListener: interactor)\n    let profileHomeRouting = profileHome.build(withListener: interactor)\n    \n    attachChild(appHomeRouting)\n    attachChild(financeHomeRouting)\n    attachChild(profileHomeRouting)\n    \n    let viewControllers = [\n      NavigationControllerable(root: appHomeRouting.viewControllable),\n      NavigationControllerable(root: financeHomeRouting.viewControllable),\n      profileHomeRouting.viewControllable\n    ]\n    \n    viewController.setViewControllers(viewControllers)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/AppRoot/RootTabBarController.swift",
    "content": "import UIKit\nimport ModernRIBs\n\nprotocol AppRootPresentableListener: AnyObject {\n  \n}\n\nfinal class RootTabBarController: UITabBarController, AppRootViewControllable, AppRootPresentable {\n  weak var listener: AppRootPresentableListener?\n  \n  override func viewDidLoad() {\n    super.viewDidLoad()\n    \n    tabBar.isTranslucent = false\n    tabBar.tintColor = .black\n    tabBar.backgroundColor = .white\n  }\n  \n  func setViewControllers(_ viewControllers: [ViewControllable]) {\n    super.setViewControllers(viewControllers.map(\\.uiviewController), animated: false)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Assets.xcassets/map_seoul.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"map_seoul.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"19162\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"19144\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Super\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"CX5-z6-Rgm\">\n                                <rect key=\"frame\" x=\"141\" y=\"425.5\" width=\"132.5\" height=\"55\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" weight=\"heavy\" pointSize=\"46\"/>\n                                <color key=\"textColor\" systemColor=\"systemRedColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"CX5-z6-Rgm\" firstAttribute=\"centerX\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerX\" id=\"39B-mV-2d6\"/>\n                            <constraint firstItem=\"CX5-z6-Rgm\" firstAttribute=\"centerY\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerY\" id=\"MVn-2c-RfV\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"52.173913043478265\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <systemColor name=\"systemRedColor\">\n            <color red=\"1\" green=\"0.23137254901960785\" blue=\"0.18823529411764706\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "MiniSuperApp/FinanceHome/FinanceHomeBuilder.swift",
    "content": "import ModernRIBs\n\nprotocol FinanceHomeDependency: Dependency {\n  // TODO: Declare the set of dependencies required by this RIB, but cannot be\n  // created by this RIB.\n}\n\nfinal class FinanceHomeComponent: Component<FinanceHomeDependency> {\n  \n  // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.\n}\n\n// MARK: - Builder\n\nprotocol FinanceHomeBuildable: Buildable {\n  func build(withListener listener: FinanceHomeListener) -> FinanceHomeRouting\n}\n\nfinal class FinanceHomeBuilder: Builder<FinanceHomeDependency>, FinanceHomeBuildable {\n  \n  override init(dependency: FinanceHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: FinanceHomeListener) -> FinanceHomeRouting {\n    let _ = FinanceHomeComponent(dependency: dependency)\n    let viewController = FinanceHomeViewController()\n    let interactor = FinanceHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    return FinanceHomeRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/FinanceHome/FinanceHomeInteractor.swift",
    "content": "import ModernRIBs\n\nprotocol FinanceHomeRouting: ViewableRouting {\n  // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.\n}\n\nprotocol FinanceHomePresentable: Presentable {\n  var listener: FinanceHomePresentableListener? { get set }\n  // TODO: Declare methods the interactor can invoke the presenter to present data.\n}\n\nprotocol FinanceHomeListener: AnyObject {\n  // TODO: Declare methods the interactor can invoke to communicate with other RIBs.\n}\n\nfinal class FinanceHomeInteractor: PresentableInteractor<FinanceHomePresentable>, FinanceHomeInteractable, FinanceHomePresentableListener {\n  \n  weak var router: FinanceHomeRouting?\n  weak var listener: FinanceHomeListener?\n  \n  // TODO: Add additional dependencies to constructor. Do not perform any logic\n  // in constructor.\n  override init(presenter: FinanceHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    // TODO: Implement business logic here.\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/FinanceHome/FinanceHomeRouter.swift",
    "content": "import ModernRIBs\n\nprotocol FinanceHomeInteractable: Interactable {\n  var router: FinanceHomeRouting? { get set }\n  var listener: FinanceHomeListener? { get set }\n}\n\nprotocol FinanceHomeViewControllable: ViewControllable {\n  // TODO: Declare methods the router invokes to manipulate the view hierarchy.\n}\n\nfinal class FinanceHomeRouter: ViewableRouter<FinanceHomeInteractable, FinanceHomeViewControllable>, FinanceHomeRouting {\n  \n  // TODO: Constructor inject child builder protocols to allow building children.\n  override init(interactor: FinanceHomeInteractable, viewController: FinanceHomeViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/FinanceHome/FinanceHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol FinanceHomePresentableListener: AnyObject {\n  // TODO: Declare properties and methods that the view controller can invoke to perform\n  // business logic, such as signIn(). This protocol is implemented by the corresponding\n  // interactor class.\n}\n\nfinal class FinanceHomeViewController: UIViewController, FinanceHomePresentable, FinanceHomeViewControllable {\n  \n  weak var listener: FinanceHomePresentableListener?\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let label: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    return label\n  }()\n  \n  func setupViews() {\n    title = \"슈퍼페이\"\n    tabBarItem = UITabBarItem(title: \"슈퍼페이\", image: UIImage(systemName: \"creditcard\"), selectedImage: UIImage(systemName: \"creditcard.fill\"))\n    label.text = \"Finance Home\"\n    view.backgroundColor = .systemBlue\n    view.addSubview(label)\n    NSLayoutConstraint.activate([\n      label.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n      label.centerYAnchor.constraint(equalTo: view.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "MiniSuperApp/ProfileHome/ProfileHomeBuilder.swift",
    "content": "import ModernRIBs\n\nprotocol ProfileHomeDependency: Dependency {\n  // TODO: Declare the set of dependencies required by this RIB, but cannot be\n  // created by this RIB.\n}\n\nfinal class ProfileHomeComponent: Component<ProfileHomeDependency> {\n  \n  // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.\n}\n\n// MARK: - Builder\n\nprotocol ProfileHomeBuildable: Buildable {\n  func build(withListener listener: ProfileHomeListener) -> ProfileHomeRouting\n}\n\nfinal class ProfileHomeBuilder: Builder<ProfileHomeDependency>, ProfileHomeBuildable {\n  \n  override init(dependency: ProfileHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: ProfileHomeListener) -> ProfileHomeRouting {\n    let _ = ProfileHomeComponent(dependency: dependency)\n    let viewController = ProfileHomeViewController()\n    let interactor = ProfileHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    return ProfileHomeRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/ProfileHome/ProfileHomeInteractor.swift",
    "content": "import ModernRIBs\n\nprotocol ProfileHomeRouting: ViewableRouting {\n  // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.\n}\n\nprotocol ProfileHomePresentable: Presentable {\n  var listener: ProfileHomePresentableListener? { get set }\n  // TODO: Declare methods the interactor can invoke the presenter to present data.\n}\n\nprotocol ProfileHomeListener: AnyObject {\n  // TODO: Declare methods the interactor can invoke to communicate with other RIBs.\n}\n\nfinal class ProfileHomeInteractor: PresentableInteractor<ProfileHomePresentable>, ProfileHomeInteractable, ProfileHomePresentableListener {\n  \n  weak var router: ProfileHomeRouting?\n  weak var listener: ProfileHomeListener?\n  \n  // TODO: Add additional dependencies to constructor. Do not perform any logic\n  // in constructor.\n  override init(presenter: ProfileHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    // TODO: Implement business logic here.\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/ProfileHome/ProfileHomeRouter.swift",
    "content": "import ModernRIBs\n\nprotocol ProfileHomeInteractable: Interactable {\n  var router: ProfileHomeRouting? { get set }\n  var listener: ProfileHomeListener? { get set }\n}\n\nprotocol ProfileHomeViewControllable: ViewControllable {\n  // TODO: Declare methods the router invokes to manipulate the view hierarchy.\n}\n\nfinal class ProfileHomeRouter: ViewableRouter<ProfileHomeInteractable, ProfileHomeViewControllable>, ProfileHomeRouting {\n  \n  // TODO: Constructor inject child builder protocols to allow building children.\n  override init(interactor: ProfileHomeInteractable, viewController: ProfileHomeViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/ProfileHome/ProfileHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol ProfileHomePresentableListener: AnyObject {\n  // TODO: Declare properties and methods that the view controller can invoke to perform\n  // business logic, such as signIn(). This protocol is implemented by the corresponding\n  // interactor class.\n}\n\nfinal class ProfileHomeViewController: UIViewController, ProfileHomePresentable, ProfileHomeViewControllable {\n  \n  weak var listener: ProfileHomePresentableListener?\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let label: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    return label\n  }()\n  \n  func setupViews() {\n    tabBarItem = UITabBarItem(title: \"프로필\", image: UIImage(systemName: \"person\"), selectedImage: UIImage(systemName: \"person.fill\"))\n    label.text = \"Profile Home\"\n    view.backgroundColor = .systemTeal\n    view.addSubview(label)\n    NSLayoutConstraint.activate([\n      label.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n      label.centerYAnchor.constraint(equalTo: view.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/TransportHomeBuilder.swift",
    "content": "import ModernRIBs\n\nprotocol TransportHomeDependency: Dependency {\n}\n\nfinal class TransportHomeComponent: Component<TransportHomeDependency> {\n\n}\n\n// MARK: - Builder\n\nprotocol TransportHomeBuildable: Buildable {\n  func build(withListener listener: TransportHomeListener) -> TransportHomeRouting\n}\n\nfinal class TransportHomeBuilder: Builder<TransportHomeDependency>, TransportHomeBuildable {\n  \n  override init(dependency: TransportHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: TransportHomeListener) -> TransportHomeRouting {\n    _ = TransportHomeComponent(dependency: dependency)\n    \n    let viewController = TransportHomeViewController()\n    \n    let interactor = TransportHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    \n    return TransportHomeRouter(\n      interactor: interactor,\n      viewController: viewController\n    )\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/TransportHomeInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport Foundation\n\nprotocol TransportHomeRouting: ViewableRouting {\n}\n\nprotocol TransportHomePresentable: Presentable {\n  var listener: TransportHomePresentableListener? { get set }\n  \n}\n\nprotocol TransportHomeListener: AnyObject {\n  func transportHomeDidTapClose()\n}\n\nprotocol TransportHomeInteractorDependency {\n}\n\nfinal class TransportHomeInteractor: PresentableInteractor<TransportHomePresentable>, TransportHomeInteractable, TransportHomePresentableListener {\n  \n  weak var router: TransportHomeRouting?\n  weak var listener: TransportHomeListener?\n  \n  \n  override init(presenter: TransportHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n  \n  func didTapBack() {\n    listener?.transportHomeDidTapClose()\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/TransportHomeRouter.swift",
    "content": "import ModernRIBs\n\nprotocol TransportHomeInteractable: Interactable {\n  var router: TransportHomeRouting? { get set }\n  var listener: TransportHomeListener? { get set }\n}\n\nprotocol TransportHomeViewControllable: ViewControllable {\n  // TODO: Declare methods the router invokes to manipulate the view hierarchy.\n}\n\nfinal class TransportHomeRouter: ViewableRouter<TransportHomeInteractable, TransportHomeViewControllable>, TransportHomeRouting {\n  \n  override init(\n    interactor: TransportHomeInteractable,\n    viewController: TransportHomeViewControllable\n  ) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/TransportHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol TransportHomePresentableListener: AnyObject {\n  func didTapBack()\n}\n\nfinal class TransportHomeViewController: UIViewController, TransportHomePresentable, TransportHomeViewControllable {\n  \n  weak var listener: TransportHomePresentableListener?\n  \n  private let mapView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFill\n    imageView.image = UIImage(named: \"map_seoul\")\n    return imageView\n  }()\n  \n  private let searchView: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.addShadowWithRoundedCorners(8)\n    view.backgroundColor = .white\n    return view\n  }()\n  \n  private let departureLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"우리집\"\n    return label\n  }()\n  \n  private let destinationLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"회사\"\n    return label\n  }()\n  \n  private let arrowImageView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.tintColor = .black\n    imageView.image = UIImage(\n      systemName: \"arrow.right\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n    )\n    return imageView\n  }()\n  \n  private let rideTypeView: RideTypeView = {\n    let view = RideTypeView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    return view\n  }()\n  \n  private let superPayView: SuperPayView = {\n    let view = SuperPayView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    return view\n  }()\n  \n  private lazy var backButton: UIButton = {\n    let button = UIButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.backgroundColor = .white\n    button.roundCorners(25)\n    button.tintColor = .black\n    button.setImage(\n      UIImage(\n        systemName: \"chevron.backward\",\n        withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n      ),\n      for: .normal\n    )\n    button.addTarget(self, action: #selector(backButtonDidTap), for: .touchUpInside)\n    return button\n  }()\n  \n  private let rideTypeStackView: UIStackView = {\n    let stack = UIStackView()\n    \n    return stack\n  }()\n  \n  private let paymentStackView: UIStackView = {\n    let stack = UIStackView()\n    \n    return stack\n  }()\n  \n  private let rideInfoPane: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.backgroundColor = .white\n    view.addShadowWithRoundedCorners()\n    return view\n  }()\n  \n  private lazy var rideConfirmButton: UIButton = {\n    let button = UIButton(type: .system)\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.setTitle(\"슈퍼택시 호출하기\", for: .normal)\n    button.backgroundColor = .primaryRed\n    button.tintColor = .white\n    button.addTarget(self, action: #selector(didTapRideConfirmButton), for: .touchUpInside)\n    button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    return button\n  }()\n  \n  private let separatorView: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.backgroundColor = .systemGray6\n    return view\n  }()\n  \n  func setSuperPayBalance(_ balanceText: String) {\n    superPayView.setBalanceText(\"잔고: \\(balanceText)원\")\n  }\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    view.addSubview(mapView)\n    view.addSubview(searchView)\n    searchView.addSubview(arrowImageView)\n    searchView.addSubview(departureLabel)\n    searchView.addSubview(destinationLabel)\n    view.addSubview(backButton)\n    view.addSubview(rideInfoPane)\n    rideInfoPane.addSubview(rideTypeView)\n    rideInfoPane.addSubview(superPayView)\n    rideInfoPane.addSubview(separatorView)\n    rideInfoPane.addSubview(rideConfirmButton)\n    \n    NSLayoutConstraint.activate([\n      mapView.topAnchor.constraint(equalTo: view.topAnchor),\n      mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      mapView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7),\n      \n      searchView.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 10),\n      searchView.centerYAnchor.constraint(equalTo: backButton.centerYAnchor),\n      searchView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      searchView.heightAnchor.constraint(equalToConstant: 50),\n      \n      departureLabel.leadingAnchor.constraint(equalTo: searchView.leadingAnchor, constant: 60),\n      departureLabel.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      destinationLabel.trailingAnchor.constraint(equalTo: searchView.trailingAnchor, constant: -60),\n      destinationLabel.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      rideInfoPane.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n      rideInfoPane.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      rideInfoPane.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      rideInfoPane.topAnchor.constraint(equalTo: mapView.bottomAnchor),\n      \n      backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),\n      backButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),\n      backButton.widthAnchor.constraint(equalToConstant: 50),\n      backButton.heightAnchor.constraint(equalToConstant: 50),\n      \n      arrowImageView.centerXAnchor.constraint(equalTo: searchView.centerXAnchor),\n      arrowImageView.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      rideTypeView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      rideTypeView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      rideTypeView.topAnchor.constraint(equalTo: rideInfoPane.topAnchor, constant: 10),\n      rideTypeView.heightAnchor.constraint(equalToConstant: 70),\n      \n      separatorView.topAnchor.constraint(equalTo: rideTypeView.bottomAnchor),\n      separatorView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor),\n      separatorView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor),\n      separatorView.heightAnchor.constraint(equalToConstant: 1),\n      \n      superPayView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      superPayView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      superPayView.topAnchor.constraint(equalTo: separatorView.bottomAnchor, constant: 0),\n      superPayView.bottomAnchor.constraint(equalTo: rideConfirmButton.topAnchor),\n      \n      rideConfirmButton.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      rideConfirmButton.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      rideConfirmButton.bottomAnchor.constraint(equalTo: rideInfoPane.safeAreaLayoutGuide.bottomAnchor, constant: -20),\n      rideConfirmButton.heightAnchor.constraint(equalToConstant: 60)\n    ])\n  }\n  \n  @objc\n  private func backButtonDidTap() {\n    listener?.didTapBack()\n  }\n  \n  @objc\n  private func didTapRideConfirmButton() {\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/Views/RideTypeView.swift",
    "content": "import UIKit\n\nfinal class RideTypeView: UIView {\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFit\n    imageView.tintColor = .black\n    imageView.image = UIImage(\n      systemName: \"bolt.car\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n    )\n    return imageView\n  }()\n  \n  private let rideTypeNameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"슈퍼전기차 택시\"\n    return label\n  }()\n  \n  private let priceLabel: UILabel = {\n    let label = UILabel()\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.text = \"18,000원\"\n    return label\n  }()\n  \n  init() {\n    super.init(frame: .zero)\n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(thumbnailView)\n    addSubview(priceLabel)\n    addSubview(rideTypeNameLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.leadingAnchor.constraint(equalTo: self.leadingAnchor),\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 40),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 40),\n      \n      rideTypeNameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      rideTypeNameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 10),\n      \n      priceLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      priceLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/TransportHome/Views/SuperPayView.swift",
    "content": "import UIKit\n\nfinal class SuperPayView: UIView {\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.backgroundColor = .systemBlue\n    imageView.roundCorners(4)\n    return imageView\n  }()\n  \n  private let nameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"슈퍼페이\"\n    return label\n  }()\n  \n  private let balanceLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"---원\"\n    return label\n  }()\n  \n  func setBalanceText(_ text: String) {\n    balanceLabel.text = text\n  }\n  \n  init() {\n    super.init(frame: .zero)\n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(thumbnailView)\n    addSubview(nameLabel)\n    addSubview(balanceLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      \n      nameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      nameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 10),\n      \n      balanceLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      balanceLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n    ])\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/Array+Utils.swift",
    "content": "import Foundation\n\nextension Array {\n  subscript(safe index: Int) -> Element? {\n    return indices ~= index ? self[index] : nil\n  }\n}\n\n"
  },
  {
    "path": "MiniSuperApp/Utils/PushModalPresentationController.swift",
    "content": "import UIKit\n\npublic final class PushModalPresentationController: NSObject, UIViewControllerTransitioningDelegate {\n  \n  public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {\n    return PushModalPresentTransitioning()\n  }\n  \n  public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {\n    return PushModalDismissTransitioning()\n  }\n  \n}\n\nprivate final class PushModalPresentTransitioning: NSObject, UIViewControllerAnimatedTransitioning {\n  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {\n    return 0.25\n  }\n  \n  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {\n    guard let toViewController = transitionContext.viewController(forKey: .to) else {\n      return\n    }\n    \n    let containerView = transitionContext.containerView\n    let toView = transitionContext.view(forKey: .to)\n    \n    var toViewInitialFrame   = transitionContext.initialFrame(for: toViewController)\n    let toViewFinalFrame     = transitionContext.finalFrame(for: toViewController)\n    toView.map(containerView.addSubview)\n    \n    toViewInitialFrame.origin = CGPoint(x: containerView.bounds.maxX, y: containerView.bounds.minY)\n    toViewInitialFrame.size = toViewFinalFrame.size\n    toView?.frame = toViewInitialFrame\n    \n    UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.allowUserInteraction, .curveEaseOut], animations: {\n      toView?.frame = toViewFinalFrame\n    }) { _ in\n      let isCompleted = !transitionContext.transitionWasCancelled\n      transitionContext.completeTransition(isCompleted)\n    }\n  }\n  \n}\n\nprivate final class PushModalDismissTransitioning: NSObject, UIViewControllerAnimatedTransitioning {\n  \n  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {\n    return 0.25\n  }\n  \n  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {\n    guard let fromViewController = transitionContext.viewController(forKey: .from) else {\n      return\n    }\n    \n    let containerView = transitionContext.containerView\n    let toView = transitionContext.view(forKey: .to)\n    let fromView = transitionContext.view(forKey: .from)\n    var fromViewFinalFrame = transitionContext.finalFrame(for: fromViewController)\n    \n    toView.map(containerView.addSubview)\n    \n    if let fromView = fromView {\n      fromViewFinalFrame = fromView.frame.offsetBy(dx: fromView.frame.width, dy: 0)\n    }\n    \n    UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.allowUserInteraction, .curveEaseOut], animations: {\n      fromView?.frame = fromViewFinalFrame\n    }) { _ in\n      let isCompleted = !transitionContext.transitionWasCancelled\n      transitionContext.completeTransition(isCompleted)\n    }\n  }\n  \n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/RIBs+Utils.swift",
    "content": "import UIKit\nimport ModernRIBs\n\nfinal class NavigationControllerable: ViewControllable {\n  \n  var uiviewController: UIViewController { self.navigationController }\n  let navigationController: UINavigationController\n  \n  public init(root: ViewControllable) {\n    let navigation = UINavigationController(rootViewController: root.uiviewController)\n    navigation.navigationBar.isTranslucent = false\n    navigation.navigationBar.backgroundColor = .white\n    navigation.navigationBar.scrollEdgeAppearance = navigation.navigationBar.standardAppearance\n    \n    self.navigationController = navigation\n  }\n}\n\nextension ViewControllable {\n  \n  func present(_ viewControllable: ViewControllable, animated: Bool, completion: (() -> Void)?) {\n    self.uiviewController.present(viewControllable.uiviewController, animated: true, completion: completion)\n  }\n  \n  func dismiss(completion: (() -> Void)?) {\n    self.uiviewController.dismiss(animated: true, completion: completion)\n  }\n  \n  func pushViewController(_ viewControllable: ViewControllable, animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.pushViewController(viewControllable.uiviewController, animated: animated)\n    } else {\n      self.uiviewController.navigationController?.pushViewController(viewControllable.uiviewController, animated: animated)\n    }\n  }\n  \n  func popViewController(animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.popViewController(animated: animated)\n    } else {\n      self.uiviewController.navigationController?.popViewController(animated: animated)\n    }\n  }\n  \n  func popToRoot(animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.popToRootViewController(animated: animated)\n    } else {\n      self.uiviewController.navigationController?.popToRootViewController(animated: animated)\n    }\n  }\n  \n  func setViewControllers(_ viewControllerables: [ViewControllable]) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.setViewControllers(viewControllerables.map(\\.uiviewController), animated: true)\n    } else {\n      self.uiviewController.navigationController?.setViewControllers(viewControllerables.map(\\.uiviewController), animated: true)\n    }\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/UIColor+Super.swift",
    "content": "import UIKit\n\nextension UIColor {\n  static let backgroundColor = UIColor(hex: \"#F1F5F9FF\")!\n  static let primaryRed = UIColor(hex: \"#eb445aff\")!\n}\n\n"
  },
  {
    "path": "MiniSuperApp/Utils/UIColor+Utils.swift",
    "content": "import UIKit\n\nextension UIColor {\n  convenience init?(hex: String) {\n    let r, g, b, a: CGFloat\n    \n    if hex.hasPrefix(\"#\") {\n      let start = hex.index(hex.startIndex, offsetBy: 1)\n      let hexColor = String(hex[start...])\n      \n      if hexColor.count == 8 {\n        let scanner = Scanner(string: hexColor)\n        var hexNumber: UInt64 = 0\n        \n        if scanner.scanHexInt64(&hexNumber) {\n          r = CGFloat((hexNumber & 0xff000000) >> 24) / 255\n          g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255\n          b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255\n          a = CGFloat(hexNumber & 0x000000ff) / 255\n          \n          self.init(red: r, green: g, blue: b, alpha: a)\n          return\n        }\n      }\n    }\n    \n    return nil\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/UIImage+Utils.swift",
    "content": "import UIKit\n\npublic extension UIImage {\n  convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {\n    let rect = CGRect(origin: .zero, size: size)\n    UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)\n    color.setFill()\n    UIRectFill(rect)\n    let image = UIGraphicsGetImageFromCurrentImageContext()\n    UIGraphicsEndImageContext()\n    \n    guard let cgImage = image?.cgImage else { return nil }\n    \n    self.init(cgImage: cgImage)\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/UITableView+Utils.swift",
    "content": "import UIKit\n\npublic protocol Reusable: AnyObject {\n  static var reuseIdentifier: String { get }\n}\n\npublic extension Reusable {\n  static var reuseIdentifier: String {\n    return String(describing: self)\n  }\n}\n\nextension UITableViewCell: Reusable {}\n\npublic extension UITableView {\n  \n  func register<T: UITableViewCell>(cellType: T.Type) {\n    self.register(cellType, forCellReuseIdentifier: T.reuseIdentifier)\n  }\n  \n  func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T {\n    guard let cell = self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {\n      fatalError(\"Failed to dequeue reusable cell\")\n    }\n    return cell\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp/Utils/UIView+Utils.swift",
    "content": "import UIKit\n\nextension UIView {\n  func addShadowWithRoundedCorners(\n    _ radius: CGFloat = 16,\n    shadowColor: CGColor = UIColor.black.cgColor,\n    opacity: Float = 0.1\n  ) {\n    self.layer.cornerCurve = .continuous\n    self.layer.masksToBounds = false\n    self.layer.shadowColor = shadowColor\n    self.layer.shadowOffset = CGSize(width: 0, height: 0)\n    self.layer.shadowOpacity = opacity\n    self.layer.shadowRadius = 2.5\n    self.layer.cornerRadius = radius\n  }\n  \n  func roundCorners(\n    _ radius: CGFloat = 16\n  ) {\n    self.layer.cornerCurve = .continuous\n    self.layer.cornerRadius = radius\n    self.clipsToBounds = true\n  }\n}\n"
  },
  {
    "path": "MiniSuperApp.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 52;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tF57EBE542738468700FE9319 /* UIImage+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57EBE512738468700FE9319 /* UIImage+Utils.swift */; };\n\t\tF57EBE552738468700FE9319 /* Array+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57EBE522738468700FE9319 /* Array+Utils.swift */; };\n\t\tF57EBE562738468700FE9319 /* UITableView+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57EBE532738468700FE9319 /* UITableView+Utils.swift */; };\n\t\tF57F6AE826DB4A2700C0117D /* FinanceHomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AE426DB4A2700C0117D /* FinanceHomeRouter.swift */; };\n\t\tF57F6AE926DB4A2700C0117D /* FinanceHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AE526DB4A2700C0117D /* FinanceHomeViewController.swift */; };\n\t\tF57F6AEA26DB4A2700C0117D /* FinanceHomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AE626DB4A2700C0117D /* FinanceHomeBuilder.swift */; };\n\t\tF57F6AEB26DB4A2700C0117D /* FinanceHomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AE726DB4A2700C0117D /* FinanceHomeInteractor.swift */; };\n\t\tF57F6AF026DB4A2D00C0117D /* ProfileHomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AEC26DB4A2D00C0117D /* ProfileHomeRouter.swift */; };\n\t\tF57F6AF126DB4A2D00C0117D /* ProfileHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AED26DB4A2D00C0117D /* ProfileHomeViewController.swift */; };\n\t\tF57F6AF226DB4A2D00C0117D /* ProfileHomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AEE26DB4A2D00C0117D /* ProfileHomeBuilder.swift */; };\n\t\tF57F6AF326DB4A2D00C0117D /* ProfileHomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AEF26DB4A2D00C0117D /* ProfileHomeInteractor.swift */; };\n\t\tF57F6AFE26DB4CD700C0117D /* RootTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */; };\n\t\tF5829F5926DB2BC400BFA8CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F5826DB2BC400BFA8CD /* AppDelegate.swift */; };\n\t\tF5829F6226DB2BC500BFA8CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F5829F6126DB2BC500BFA8CD /* Assets.xcassets */; };\n\t\tF5829F6526DB2BC500BFA8CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */; };\n\t\tF5829F6E26DB2DE900BFA8CD /* ModernRIBs in Frameworks */ = {isa = PBXBuildFile; productRef = F5829F6D26DB2DE900BFA8CD /* ModernRIBs */; };\n\t\tF5829F7426DB34AA00BFA8CD /* AppRootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */; };\n\t\tF5829F7626DB34AA00BFA8CD /* AppRootBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */; };\n\t\tF5829F7726DB34AA00BFA8CD /* AppRootInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */; };\n\t\tF5829F7926DB397300BFA8CD /* AppComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7826DB397300BFA8CD /* AppComponent.swift */; };\n\t\tF5EAA219270B395900EF2B70 /* HomeWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA213270B395800EF2B70 /* HomeWidgetView.swift */; };\n\t\tF5EAA21A270B395900EF2B70 /* AppHomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA214270B395800EF2B70 /* AppHomeBuilder.swift */; };\n\t\tF5EAA21B270B395900EF2B70 /* AppHomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA215270B395900EF2B70 /* AppHomeInteractor.swift */; };\n\t\tF5EAA21C270B395900EF2B70 /* AppHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA216270B395900EF2B70 /* AppHomeViewController.swift */; };\n\t\tF5EAA21D270B395900EF2B70 /* HomeWidgetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA217270B395900EF2B70 /* HomeWidgetModel.swift */; };\n\t\tF5EAA21E270B395900EF2B70 /* AppHomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA218270B395900EF2B70 /* AppHomeRouter.swift */; };\n\t\tF5EAA220270B39DC00EF2B70 /* PushModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA21F270B39DC00EF2B70 /* PushModalPresentationController.swift */; };\n\t\tF5EAA229270B3A4500EF2B70 /* TransportHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA222270B3A4500EF2B70 /* TransportHomeViewController.swift */; };\n\t\tF5EAA22A270B3A4500EF2B70 /* TransportHomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA223270B3A4500EF2B70 /* TransportHomeRouter.swift */; };\n\t\tF5EAA22B270B3A4500EF2B70 /* TransportHomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA224270B3A4500EF2B70 /* TransportHomeBuilder.swift */; };\n\t\tF5EAA22C270B3A4500EF2B70 /* TransportHomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA225270B3A4500EF2B70 /* TransportHomeInteractor.swift */; };\n\t\tF5EAA22D270B3A4500EF2B70 /* RideTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA227270B3A4500EF2B70 /* RideTypeView.swift */; };\n\t\tF5EAA22E270B3A4500EF2B70 /* SuperPayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA228270B3A4500EF2B70 /* SuperPayView.swift */; };\n\t\tF5EAA230270B3A7100EF2B70 /* RIBs+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA22F270B3A7100EF2B70 /* RIBs+Utils.swift */; };\n\t\tF5EAA232270B3A8F00EF2B70 /* UIView+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA231270B3A8F00EF2B70 /* UIView+Utils.swift */; };\n\t\tF5EAA234270B3A9A00EF2B70 /* UIColor+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA233270B3A9A00EF2B70 /* UIColor+Utils.swift */; };\n\t\tF5EAA236270B3AA600EF2B70 /* UIColor+Super.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EAA235270B3AA600EF2B70 /* UIColor+Super.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tF57EBE512738468700FE9319 /* UIImage+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIImage+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF57EBE522738468700FE9319 /* Array+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"Array+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF57EBE532738468700FE9319 /* UITableView+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UITableView+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF57F6AE426DB4A2700C0117D /* FinanceHomeRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinanceHomeRouter.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AE526DB4A2700C0117D /* FinanceHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinanceHomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AE626DB4A2700C0117D /* FinanceHomeBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinanceHomeBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AE726DB4A2700C0117D /* FinanceHomeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinanceHomeInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AEC26DB4A2D00C0117D /* ProfileHomeRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHomeRouter.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AED26DB4A2D00C0117D /* ProfileHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AEE26DB4A2D00C0117D /* ProfileHomeBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHomeBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AEF26DB4A2D00C0117D /* ProfileHomeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHomeInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootTabBarController.swift; sourceTree = \"<group>\"; };\n\t\tF5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MiniSuperApp.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF5829F5826DB2BC400BFA8CD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF5829F6126DB2BC500BFA8CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF5829F6426DB2BC500BFA8CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tF5829F6626DB2BC500BFA8CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tF5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootRouter.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7826DB397300BFA8CD /* AppComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppComponent.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA213270B395800EF2B70 /* HomeWidgetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeWidgetView.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA214270B395800EF2B70 /* AppHomeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHomeBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA215270B395900EF2B70 /* AppHomeInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHomeInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA216270B395900EF2B70 /* AppHomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA217270B395900EF2B70 /* HomeWidgetModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeWidgetModel.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA218270B395900EF2B70 /* AppHomeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHomeRouter.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA21F270B39DC00EF2B70 /* PushModalPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushModalPresentationController.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA222270B3A4500EF2B70 /* TransportHomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportHomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA223270B3A4500EF2B70 /* TransportHomeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportHomeRouter.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA224270B3A4500EF2B70 /* TransportHomeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportHomeBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA225270B3A4500EF2B70 /* TransportHomeInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportHomeInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA227270B3A4500EF2B70 /* RideTypeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RideTypeView.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA228270B3A4500EF2B70 /* SuperPayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperPayView.swift; sourceTree = \"<group>\"; };\n\t\tF5EAA22F270B3A7100EF2B70 /* RIBs+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"RIBs+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF5EAA231270B3A8F00EF2B70 /* UIView+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIView+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF5EAA233270B3A9A00EF2B70 /* UIColor+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIColor+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF5EAA235270B3AA600EF2B70 /* UIColor+Super.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIColor+Super.swift\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tF5829F5226DB2BC400BFA8CD /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5829F6E26DB2DE900BFA8CD /* ModernRIBs in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tF57F6AD926DB49E500C0117D /* AppHome */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5EAA214270B395800EF2B70 /* AppHomeBuilder.swift */,\n\t\t\t\tF5EAA215270B395900EF2B70 /* AppHomeInteractor.swift */,\n\t\t\t\tF5EAA218270B395900EF2B70 /* AppHomeRouter.swift */,\n\t\t\t\tF5EAA216270B395900EF2B70 /* AppHomeViewController.swift */,\n\t\t\t\tF5EAA217270B395900EF2B70 /* HomeWidgetModel.swift */,\n\t\t\t\tF5EAA212270B395800EF2B70 /* Views */,\n\t\t\t);\n\t\t\tpath = AppHome;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF57F6ADA26DB49EA00C0117D /* FinanceHome */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF57F6AE426DB4A2700C0117D /* FinanceHomeRouter.swift */,\n\t\t\t\tF57F6AE526DB4A2700C0117D /* FinanceHomeViewController.swift */,\n\t\t\t\tF57F6AE626DB4A2700C0117D /* FinanceHomeBuilder.swift */,\n\t\t\t\tF57F6AE726DB4A2700C0117D /* FinanceHomeInteractor.swift */,\n\t\t\t);\n\t\t\tpath = FinanceHome;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF57F6ADB26DB49FD00C0117D /* ProfileHome */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF57F6AEC26DB4A2D00C0117D /* ProfileHomeRouter.swift */,\n\t\t\t\tF57F6AED26DB4A2D00C0117D /* ProfileHomeViewController.swift */,\n\t\t\t\tF57F6AEE26DB4A2D00C0117D /* ProfileHomeBuilder.swift */,\n\t\t\t\tF57F6AEF26DB4A2D00C0117D /* ProfileHomeInteractor.swift */,\n\t\t\t);\n\t\t\tpath = ProfileHome;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF57F6AFF26DB585100C0117D /* Utils */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF57EBE522738468700FE9319 /* Array+Utils.swift */,\n\t\t\t\tF57EBE512738468700FE9319 /* UIImage+Utils.swift */,\n\t\t\t\tF57EBE532738468700FE9319 /* UITableView+Utils.swift */,\n\t\t\t\tF5EAA235270B3AA600EF2B70 /* UIColor+Super.swift */,\n\t\t\t\tF5EAA233270B3A9A00EF2B70 /* UIColor+Utils.swift */,\n\t\t\t\tF5EAA231270B3A8F00EF2B70 /* UIView+Utils.swift */,\n\t\t\t\tF5EAA21F270B39DC00EF2B70 /* PushModalPresentationController.swift */,\n\t\t\t\tF5EAA22F270B3A7100EF2B70 /* RIBs+Utils.swift */,\n\t\t\t);\n\t\t\tpath = Utils;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F4C26DB2BC400BFA8CD = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F5726DB2BC400BFA8CD /* MiniSuperApp */,\n\t\t\t\tF5829F5626DB2BC400BFA8CD /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F5626DB2BC400BFA8CD /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F5726DB2BC400BFA8CD /* MiniSuperApp */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F7A26DB3AF900BFA8CD /* AppDelegate */,\n\t\t\t\tF5829F6F26DB33CF00BFA8CD /* AppRoot */,\n\t\t\t\tF57F6AD926DB49E500C0117D /* AppHome */,\n\t\t\t\tF5EAA221270B3A4500EF2B70 /* TransportHome */,\n\t\t\t\tF57F6ADA26DB49EA00C0117D /* FinanceHome */,\n\t\t\t\tF57F6ADB26DB49FD00C0117D /* ProfileHome */,\n\t\t\t\tF57F6AFF26DB585100C0117D /* Utils */,\n\t\t\t\tF5829F6126DB2BC500BFA8CD /* Assets.xcassets */,\n\t\t\t\tF5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */,\n\t\t\t\tF5829F6626DB2BC500BFA8CD /* Info.plist */,\n\t\t\t);\n\t\t\tpath = MiniSuperApp;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F6F26DB33CF00BFA8CD /* AppRoot */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */,\n\t\t\t\tF5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */,\n\t\t\t\tF5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */,\n\t\t\t\tF57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */,\n\t\t\t);\n\t\t\tpath = AppRoot;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F7A26DB3AF900BFA8CD /* AppDelegate */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F5826DB2BC400BFA8CD /* AppDelegate.swift */,\n\t\t\t\tF5829F7826DB397300BFA8CD /* AppComponent.swift */,\n\t\t\t);\n\t\t\tpath = AppDelegate;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5EAA212270B395800EF2B70 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5EAA213270B395800EF2B70 /* HomeWidgetView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5EAA221270B3A4500EF2B70 /* TransportHome */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5EAA222270B3A4500EF2B70 /* TransportHomeViewController.swift */,\n\t\t\t\tF5EAA223270B3A4500EF2B70 /* TransportHomeRouter.swift */,\n\t\t\t\tF5EAA224270B3A4500EF2B70 /* TransportHomeBuilder.swift */,\n\t\t\t\tF5EAA225270B3A4500EF2B70 /* TransportHomeInteractor.swift */,\n\t\t\t\tF5EAA226270B3A4500EF2B70 /* Views */,\n\t\t\t);\n\t\t\tpath = TransportHome;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5EAA226270B3A4500EF2B70 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5EAA227270B3A4500EF2B70 /* RideTypeView.swift */,\n\t\t\t\tF5EAA228270B3A4500EF2B70 /* SuperPayView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tF5829F5426DB2BC400BFA8CD /* MiniSuperApp */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F5829F6926DB2BC500BFA8CD /* Build configuration list for PBXNativeTarget \"MiniSuperApp\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF5829F5126DB2BC400BFA8CD /* Sources */,\n\t\t\t\tF5829F5226DB2BC400BFA8CD /* Frameworks */,\n\t\t\t\tF5829F5326DB2BC400BFA8CD /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MiniSuperApp;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF5829F6D26DB2DE900BFA8CD /* ModernRIBs */,\n\t\t\t);\n\t\t\tproductName = SuperRedApp;\n\t\t\tproductReference = F5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tF5829F4D26DB2BC400BFA8CD /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1250;\n\t\t\t\tLastUpgradeCheck = 1250;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tF5829F5426DB2BC400BFA8CD = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.5.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = F5829F5026DB2BC400BFA8CD /* Build configuration list for PBXProject \"MiniSuperApp\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = F5829F4C26DB2BC400BFA8CD;\n\t\t\tpackageReferences = (\n\t\t\t\tF5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */,\n\t\t\t);\n\t\t\tproductRefGroup = F5829F5626DB2BC400BFA8CD /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tF5829F5426DB2BC400BFA8CD /* MiniSuperApp */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tF5829F5326DB2BC400BFA8CD /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5829F6526DB2BC500BFA8CD /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tF5829F6226DB2BC500BFA8CD /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tF5829F5126DB2BC400BFA8CD /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5EAA229270B3A4500EF2B70 /* TransportHomeViewController.swift in Sources */,\n\t\t\t\tF57F6AF226DB4A2D00C0117D /* ProfileHomeBuilder.swift in Sources */,\n\t\t\t\tF5EAA22A270B3A4500EF2B70 /* TransportHomeRouter.swift in Sources */,\n\t\t\t\tF5EAA232270B3A8F00EF2B70 /* UIView+Utils.swift in Sources */,\n\t\t\t\tF57F6AEA26DB4A2700C0117D /* FinanceHomeBuilder.swift in Sources */,\n\t\t\t\tF5EAA22E270B3A4500EF2B70 /* SuperPayView.swift in Sources */,\n\t\t\t\tF5EAA220270B39DC00EF2B70 /* PushModalPresentationController.swift in Sources */,\n\t\t\t\tF57F6AF126DB4A2D00C0117D /* ProfileHomeViewController.swift in Sources */,\n\t\t\t\tF57F6AF326DB4A2D00C0117D /* ProfileHomeInteractor.swift in Sources */,\n\t\t\t\tF5829F7626DB34AA00BFA8CD /* AppRootBuilder.swift in Sources */,\n\t\t\t\tF5EAA230270B3A7100EF2B70 /* RIBs+Utils.swift in Sources */,\n\t\t\t\tF57EBE542738468700FE9319 /* UIImage+Utils.swift in Sources */,\n\t\t\t\tF5EAA22B270B3A4500EF2B70 /* TransportHomeBuilder.swift in Sources */,\n\t\t\t\tF5EAA21D270B395900EF2B70 /* HomeWidgetModel.swift in Sources */,\n\t\t\t\tF5EAA21E270B395900EF2B70 /* AppHomeRouter.swift in Sources */,\n\t\t\t\tF5EAA21A270B395900EF2B70 /* AppHomeBuilder.swift in Sources */,\n\t\t\t\tF5EAA234270B3A9A00EF2B70 /* UIColor+Utils.swift in Sources */,\n\t\t\t\tF5EAA22C270B3A4500EF2B70 /* TransportHomeInteractor.swift in Sources */,\n\t\t\t\tF5829F7726DB34AA00BFA8CD /* AppRootInteractor.swift in Sources */,\n\t\t\t\tF5EAA22D270B3A4500EF2B70 /* RideTypeView.swift in Sources */,\n\t\t\t\tF5EAA219270B395900EF2B70 /* HomeWidgetView.swift in Sources */,\n\t\t\t\tF57F6AFE26DB4CD700C0117D /* RootTabBarController.swift in Sources */,\n\t\t\t\tF57F6AEB26DB4A2700C0117D /* FinanceHomeInteractor.swift in Sources */,\n\t\t\t\tF5EAA21B270B395900EF2B70 /* AppHomeInteractor.swift in Sources */,\n\t\t\t\tF57F6AE826DB4A2700C0117D /* FinanceHomeRouter.swift in Sources */,\n\t\t\t\tF57F6AE926DB4A2700C0117D /* FinanceHomeViewController.swift in Sources */,\n\t\t\t\tF5829F5926DB2BC400BFA8CD /* AppDelegate.swift in Sources */,\n\t\t\t\tF5EAA21C270B395900EF2B70 /* AppHomeViewController.swift in Sources */,\n\t\t\t\tF5EAA236270B3AA600EF2B70 /* UIColor+Super.swift in Sources */,\n\t\t\t\tF5829F7926DB397300BFA8CD /* AppComponent.swift in Sources */,\n\t\t\t\tF57F6AF026DB4A2D00C0117D /* ProfileHomeRouter.swift in Sources */,\n\t\t\t\tF57EBE562738468700FE9319 /* UITableView+Utils.swift in Sources */,\n\t\t\t\tF57EBE552738468700FE9319 /* Array+Utils.swift in Sources */,\n\t\t\t\tF5829F7426DB34AA00BFA8CD /* AppRootRouter.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tF5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F6426DB2BC500BFA8CD /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tF5829F6726DB2BC500BFA8CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5829F6826DB2BC500BFA8CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF5829F6A26DB2BC500BFA8CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = MiniSuperApp/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5829F6B26DB2BC500BFA8CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = MiniSuperApp/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tF5829F5026DB2BC400BFA8CD /* Build configuration list for PBXProject \"MiniSuperApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5829F6726DB2BC500BFA8CD /* Debug */,\n\t\t\t\tF5829F6826DB2BC500BFA8CD /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF5829F6926DB2BC500BFA8CD /* Build configuration list for PBXNativeTarget \"MiniSuperApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5829F6A26DB2BC500BFA8CD /* Debug */,\n\t\t\t\tF5829F6B26DB2BC500BFA8CD /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\tF5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/DevYeom/ModernRIBs\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.0.1;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\tF5829F6D26DB2DE900BFA8CD /* ModernRIBs */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */;\n\t\t\tproductName = ModernRIBs;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = F5829F4D26DB2BC400BFA8CD /* Project object */;\n}\n"
  },
  {
    "path": "MiniSuperApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "MiniSuperApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "MiniSuperApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"ModernRIBs\",\n        \"repositoryURL\": \"https://github.com/DevYeom/ModernRIBs\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"5e0a67365a1fb18ca06b919dbf53608843ddc284\",\n          \"version\": \"1.0.1\"\n        }\n      }\n    ]\n  },\n  \"version\": 1\n}\n"
  },
  {
    "path": "MiniSuperApp.xcodeproj/xcshareddata/xcschemes/MiniSuperApp.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1250\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n               BuildableName = \"MiniSuperApp.app\"\n               BlueprintName = \"MiniSuperApp\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n            BuildableName = \"MiniSuperApp.app\"\n            BlueprintName = \"MiniSuperApp\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n            BuildableName = \"MiniSuperApp.app\"\n            BlueprintName = \"MiniSuperApp\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://fastcampus.co.kr/dev_red_rsj?utm_source=soojin-github&utm_medium=readme&utm_campaign=soojin\">\n  <img src=\"https://soojin.ro/assets/posts/fastcampus-0.png\" />\n</a>\n\n<div align = \"center\">\n  <a href=\"https://fastcampus.co.kr/dev_red_rsj?utm_source=soojin-github&utm_medium=readme&utm_campaign=soojin\">\n    <img src=\"https://img.shields.io/badge/강의-패스트캠퍼스-red?style=flat\" />\n  </a>\n  <a href=\"https://soojin.ro\">\n    <img src=\"https://img.shields.io/badge/iOS개발자-노수진-orange?style=flat\" />\n  </a>\n  <a href=\"https://github.com/nsoojin/MiniSuperApp-fastcampus\">\n    <img src=\"https://img.shields.io/badge/실습 프로젝트-미니슈퍼앱-378805?style=flat\" />\n  </a>\n  <a href=\"https://github.com/nsoojin/MiniSuperApp-fastcampus/discussions/categories/아무-질문이나-환영합니다\">\n    <img src=\"https://img.shields.io/badge/질문-환영-ffda00?style=flat\" />\n  </a>\n</div>\n\n### \"모바일 개발자에게 확장성(scalability)이란\n\n모바일 팀과 앱의 규모가 계속 커져도 사용자 경험과 개발자 경험 모두를 안정적으로 유지하는 것이라고 생각합니다.\n\n개발자의 기술력은 개발 과정에서 발생하는 병목 현상을 얼마나 잘 처리하는지에서 보여지죠. 서버의 경우에는 많은 사용자가 몰릴 때 병목 현상이 발생하지만, 모바일의 경우에는 하나의 프로그램에 다수의 개발자들의 코드가 몰릴 때 병목이 발생한다고 볼 수 있습니다.\"\n\n관련글: [모바일 개발자에게 scalability란 뭘까](https://soojin.ro/blog/scalability)\n<br>\n\n# 강의 내용\n\n### 1부. 코드 레벨 아키텍처: 재사용 가능한 코드를 만드는 스킬\n\n객체를 작게 만들고, 작은 객체를 조합해서 복잡한 기능으로 합치는 것이 아키텍처의 시작입니다. Massive View Controller, Massive View Model, Massive Interactor는 아키텍처만의 문제가 아니라 개발자의 [composition](https://en.wikipedia.org/wiki/Object_composition) 활용 능력에 따라 달라질 수 있습니다. Composition이 강력한 아키텍처 프레임워크 RIBs를 기반으로 미니 슈퍼앱을 만들어봅니다.\n\n관련1: [스위프트로 다시보는 객체지향 프로그래밍: 피해야할 코딩 습관](https://soojin.ro/blog/solid-principles-in-swift)\n<br>\n관련2: [개발자와 라면 조리법](https://soojin.ro/blog/programmer-and-ramyun)\n<br>\n관련3: [google/promises를 활용한 스위프트 비동기 프로그래밍과 에러 핸들링](https://soojin.ro/blog/using-google-promises-swift)\n\n### 2부. 모듈 레벨 아키텍처: 유지 보수와 개발 속도를 고려하는 모듈화\n\n'느슨하게 결합된 모듈 구조'는 '확장성 있는 아키텍처'와 같은 말이나 다름없습니다. 200명의 iOS 앱 개발자가 기여하는 슈퍼앱 그랩, 약 75명이 기여하는 [에어비엔비](https://medium.com/airbnb-engineering/designing-for-productivity-in-a-large-scale-ios-application-9376a430a0bf) 같은 회사의 개발자들이 생산성을 지킬 수 있는 방법입니다. 왜 모듈화를 하면 빌드 시간이 줄어들어서 생산성이 오르는지 원리를 알아보고, 실습을 통해 미니 슈퍼앱에 적용해봅니다.\n\n관련1: [모바일 앱의 느슨한 결합](https://soojin.ro/blog/loose-coupling)\n<br>\n관련2: [Sourcery 개발자로부터 배우는 모바일 아키텍처와 개발자 경험](https://soojin.ro/blog/pragmatic-programmer)\n\n### 3부. 자동화 테스팅\n\n현업 개발자들이 테스트를 처음 시작하기 어려운 이유는 레거시 코드가 테스트 불가능한 구조로 짜여져 있기 때문입니다. 하지만 실습에서 짜는 코드는 99% 테스트 가능한 코드입니다. 테스트 가능한 코드의 특징을 한번이라도 익히고 직접 테스트를 작성해보면 레거시 코드에 조금씩 도입하기도 쉽습니다. 유닛테스트, 스냅샷테스트, UI테스트, 통합테스트를 작성해봅니다.\n\n관련1: [테스트와 좋은 설계의 관계, 그리고 나쁜 설계의 영향](https://soojin.ro/blog/tests-and-design)\n<br>\n관련2: [테스트 코드 작성하면 좋은 점](https://soojin.ro/blog/writing-test-code)\n<br>\n관련3: [uber/RIBs 유닛 테스트 짜기](https://soojin.ro/blog/unit-testing-ribs)\n<br>\n관련4: [XCTest 소요시간 단축하기](https://soojin.ro/blog/application-library-test)\n\n### 4부. 확장성 있는 인프라: 코드만으로 해결할 수 없는 문제들\n\n확장성 있는 아키텍처를 만들고 유지하려면 코드 뿐 아니라 개발 프로세스도 뒷받침해줘야 합니다. 피쳐플래그와 품질 모니터링을 도입해서 얻을 수 있는 것들과 제가 경험해본 좋은 개발 문화 사례를 공유합니다.\n\n관련1: [앱 안정성을 향한 끊임없는 여정](https://soojin.ro/blog/journey-to-app-stability)\n<br>\n관련2: [팀워크](https://soojin.ro/blog/teamwork)\n<br>\n관련3: [개인과 팀이 성장하는 모바일 개발 환경](https://soojin.ro/blog/mobile-platform)\n"
  },
  {
    "path": "Samples/DefaultsStore/DefaultsStore.swift",
    "content": "import Foundation\n\npublic protocol DefaultsStore {\n  var isInitialLaunch: Bool { get set }\n  var lastNoticeDate: Double { get set }\n}\n\npublic struct DefaultsStoreImp: DefaultsStore {\n  \n  public var isInitialLaunch: Bool {\n    get {\n      userDefaults.bool(forKey: kIsInitialLaunch)\n    }\n    set {\n      userDefaults.set(newValue, forKey: kIsInitialLaunch)\n    }\n  }\n  \n  public var lastNoticeDate: Double {\n    get {\n      userDefaults.double(forKey: kLastNoticeDate)\n    }\n    set {\n      userDefaults.set(newValue, forKey: kLastNoticeDate)\n    }\n  }\n  \n  private let userDefaults: UserDefaults\n  \n  private let kIsInitialLaunch = \"kIsInitialLaunch\"\n  private let kLastNoticeDate = \"kLastNoticeDate\"\n  \n  public init(defaults: UserDefaults) {\n    self.userDefaults = defaults\n  }\n  \n}\n"
  },
  {
    "path": "Samples/Network/HTTPMethod.swift",
    "content": "import Foundation\n\npublic enum HTTPMethod: String, Encodable {\n  case get = \"GET\"\n  case post = \"POST\"\n  case put = \"PUT\"\n}\n"
  },
  {
    "path": "Samples/Network/Network.swift",
    "content": "import Combine\nimport Foundation\n\npublic typealias QueryItems = [String: AnyHashable]\npublic typealias HTTPHeader = [String: String]\n\npublic protocol Request: Hashable {\n  associatedtype Output: Decodable\n  \n  var endpoint: URL { get }\n  var method: HTTPMethod { get }\n  var query: QueryItems { get }\n  var header: HTTPHeader { get }\n}\n\npublic protocol Network {\n  func send<T: Request>(_ request: T) -> AnyPublisher<Response<T.Output>, Error>\n}\n\npublic struct Response<T: Decodable> {\n  public let output: T\n  public let statusCode: Int\n  \n  public init(output: T, statusCode: Int) {\n    self.output = output\n    self.statusCode = statusCode\n  }\n}\n"
  },
  {
    "path": "Samples/Network/NetworkError.swift",
    "content": "import Foundation\n\npublic enum NetworkError: Error {\n  case invalidURL(url: String?)\n}\n"
  },
  {
    "path": "Samples/NetworkImp/NetworkImp.swift",
    "content": "import Foundation\nimport Network\nimport Combine\n\npublic final class NetworkImp: Network {\n  \n  private let session: URLSession\n  \n  public init(\n    session: URLSession\n  ) {\n    self.session = session\n  }\n  \n  public func send<T>(_ request: T) -> AnyPublisher<Response<T.Output>, Error> where T: Request {\n    do {\n      let urlRequest = try RequestFactory(request: request).urlRequestRepresentation()\n      return session.dataTaskPublisher(for: urlRequest)\n        .tryMap { data, response in\n          let output = try JSONDecoder().decode(T.Output.self, from: data)\n          return Response(output: output, statusCode: (response as? HTTPURLResponse)?.statusCode ?? 0)\n        }\n        .eraseToAnyPublisher()\n    } catch {\n      return Fail(error: error).eraseToAnyPublisher()\n    }\n  }\n  \n}\n\nprivate final class RequestFactory<T: Request> {\n  \n  let request: T\n  private var urlComponents: URLComponents?\n  \n  init(request: T) {\n    self.request = request\n    self.urlComponents = URLComponents(url: request.endpoint, resolvingAgainstBaseURL: true)\n  }\n  \n  func urlRequestRepresentation() throws -> URLRequest {\n    switch request.method {\n    case .get:\n      return try makeGetRequest()\n    case .post:\n      return try makePostRequest()\n    case .put:\n      return try makePutRequest()\n    }\n  }\n  \n  private func makeGetRequest() throws -> URLRequest {\n    if request.query.isEmpty == false {\n      urlComponents?.queryItems = request.query.map { URLQueryItem(name: $0.key, value: \"\\($0.value)\") }\n    }\n    return try makeURLRequest()\n  }\n  \n  private func makePostRequest() throws -> URLRequest {\n    let body = try JSONSerialization.data(withJSONObject: request.query, options: [])\n    return try makeURLRequest(httpBody: body)\n  }\n  \n  private func makePutRequest() throws -> URLRequest {\n    if request.query.isEmpty == false {\n      urlComponents?.queryItems = request.query.map { URLQueryItem(name: $0.key, value: \"\\($0.value)\") }\n    }\n    return try makeURLRequest()\n  }\n  \n  private func makeURLRequest(httpBody: Data? = nil) throws -> URLRequest {\n    guard let url = urlComponents?.url else {\n      throw NetworkError.invalidURL(url: request.endpoint.absoluteString)\n    }\n    \n    var urlRequest = URLRequest(url: url)\n    request.header.forEach {\n      urlRequest.setValue($0.value, forHTTPHeaderField: $0.key)\n    }\n    urlRequest.httpMethod = request.method.rawValue\n    urlRequest.httpBody = httpBody\n    \n    return urlRequest\n  }\n}\n"
  },
  {
    "path": "Samples/RIBsTestSupport/RoutingMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport Combine\n\npublic final class RoutingMock: Routing {\n  \n  public var loadHandler: (() -> ())?\n  public var loadCallCount: Int = 0\n  public var attachChildHandler: ((_ child: Routing) -> ())?\n  public var attachChildCallCount: Int = 0\n  public var detachChildHandler: ((_ child: Routing) -> ())?\n  public var detachChildCallCount: Int = 0\n  \n  public var interactable: Interactable\n  \n  public var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  public var childrenSetCallCount = 0\n  \n  public init(\n    interactable: Interactable\n  ) {\n    self.interactable = interactable\n  }\n  \n  public func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n  \n  public func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n\n  public func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n  \n  public var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  public var lifecycleSubjectSetCallCount = 0\n  public var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n}\n"
  },
  {
    "path": "Samples/RIBsTestSupport/ViewControllableMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport UIKit\n\npublic final class ViewControllableMock: UIViewController, ViewControllable {\n  \n  public init() {\n    super.init(nibName: nil, bundle: nil)\n  }\n  \n  required init?(coder: NSCoder) {\n    fatalError(\"init(coder:) has not been implemented\")\n  }\n  \n  public var presentCallCount = 0\n  public override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {\n    presentCallCount += 1\n  }\n  \n  public var dismissCallCount = 0\n  public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {\n    dismissCallCount += 1\n  }\n\n}\n"
  },
  {
    "path": "Samples/RIBsTestSupport/ViewableRoutingMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport Combine\n\nopen class ViewableRoutingMock: ViewableRouting {\n  // Variables\n  public var viewControllable: ViewControllable\n  public var interactable: Interactable { didSet { interactableSetCallCount += 1 } }\n  public var interactableSetCallCount = 0\n  public var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  public var childrenSetCallCount = 0\n  public var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  public var lifecycleSubjectSetCallCount = 0\n  public var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n\n  // Function Handlers\n  public var loadHandler: (() -> ())?\n  public var loadCallCount: Int = 0\n  public var attachChildHandler: ((_ child: Routing) -> ())?\n  public var attachChildCallCount: Int = 0\n  public var detachChildHandler: ((_ child: Routing) -> ())?\n  public var detachChildCallCount: Int = 0\n\n  public init(\n    interactable: Interactable,\n    viewControllable: ViewControllable\n  ) {\n    self.interactable = interactable\n    self.viewControllable = viewControllable\n  }\n\n  public func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n\n  public func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n\n  public func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n}\n"
  },
  {
    "path": "Samples/TestUtil.swift",
    "content": "import Foundation\n\nenum TestUtilError: Error {\n  case fileNotFound\n}\n\nfinal class TestUtil {\n  static func path(for fileName: String, in bundleClass: AnyClass) throws -> String {\n    if let path = Bundle(for: bundleClass).path(forResource: fileName, ofType: nil) {\n      return path\n    } else {\n      throw TestUtilError.fileNotFound\n    }\n  }\n}\n"
  },
  {
    "path": "Samples/Topup/CardOnFileCell.swift",
    "content": "import UIKit\n\nfinal class CardOnFileCell: UITableViewCell {\n  \n  func setImage(_ image: UIImage?) {\n    thumbnailView.image = image\n  }\n  \n  func setTitle(_ title: String) {\n    titleLabel.text = title\n  }\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFill\n    imageView.clipsToBounds = true\n    imageView.roundCorners(4)\n    return imageView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {\n    super.init(style: style, reuseIdentifier: reuseIdentifier)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    contentView.addSubview(thumbnailView)\n    contentView.addSubview(titleLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      thumbnailView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),\n      \n      titleLabel.centerYAnchor.constraint(equalTo: thumbnailView.centerYAnchor),\n      titleLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 14)\n    ])\n  }\n  \n}\n"
  },
  {
    "path": "Samples/Topup/CardOnFileViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol CardOnFilePresentableListener: AnyObject {\n  func didTapClose()\n  func didSelectItem(at: Int)\n}\n\nfinal class CardOnFileViewController: UIViewController, CardOnFilePresentable, CardOnFileViewControllable, UITableViewDataSource, UITableViewDelegate {\n  \n  weak var listener: CardOnFilePresentableListener?\n  \n  func update(with viewModels: [PaymentMethodViewModel]) {\n    self.viewModels = viewModels\n    tableView.reloadData()\n  }\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private var viewModels: [PaymentMethodViewModel] = []\n  \n  private lazy var tableView: UITableView = {\n    let tableView = UITableView()\n    tableView.translatesAutoresizingMaskIntoConstraints = false\n    tableView.dataSource = self\n    tableView.delegate = self\n    tableView.register(cellType: CardOnFileCell.self)\n    tableView.tableFooterView = UIView()\n    tableView.rowHeight = 60\n    tableView.separatorInset = .zero\n    return tableView\n  }()\n  \n  private func setupViews() {\n    title = \"카드 선택\"\n    view.backgroundColor = .white\n    view.addSubview(tableView)\n    \n    setupNavigationItem(with: .back, target: self, action: #selector(didTapClose))\n    \n    NSLayoutConstraint.activate([\n      tableView.topAnchor.constraint(equalTo: view.topAnchor),\n      tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n    ])\n  }\n  \n  // MARK: - UITableView\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return viewModels.count + 1\n  }\n  \n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    let cell: CardOnFileCell = tableView.dequeueReusableCell(for: indexPath)\n\n    if let viewModel = viewModels[safe: indexPath.row] {\n      cell.setImage(UIImage(color: viewModel.color))\n      cell.setTitle(\"\\(viewModel.name) \\(viewModel.digits)\")\n    } else {\n      cell.setImage(UIImage(systemName: \"plus.rectangle\"))\n      cell.setTitle(\"카드 추가\")\n    }\n\n    return cell\n  }\n  \n  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n    tableView.deselectRow(at: indexPath, animated: true)\n    \n    listener?.didSelectItem(at: indexPath.row)\n  }\n  \n  @objc\n  private func didTapClose() {\n    listener?.didTapClose()\n  }\n  \n}\n"
  },
  {
    "path": "Samples/Topup/EnterAmountViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol EnterAmountPresentableListener: AnyObject {\n  func didTapClose()\n  func didTapPaymentMethod()\n  func didTapTopup(with amount: Double)\n}\n\nfinal class EnterAmountViewController: UIViewController, EnterAmountPresentable, EnterAmountViewControllable {\n  \n  weak var listener: EnterAmountPresentableListener?\n  \n  func updateSelectedPaymentMethod(with viewModel: SelectedPaymentMethodViewModel) {\n    selectedPaymentMethodView.update(with: viewModel)\n  }\n  \n  func startLoading() {\n    activityIndicator.startAnimating()\n    ctaButton.isEnabled = false\n  }\n  \n  func stopLoading() {\n    activityIndicator.stopAnimating()\n    ctaButton.isEnabled = true\n  }\n  \n  private lazy var selectedPaymentMethodView: SelectedPaymentMethodView = {\n    let view = SelectedPaymentMethodView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.addShadowWithRoundedCorners()\n    let tap = UITapGestureRecognizer(target: self, action: #selector(didTapPaymentMethod))\n    view.addGestureRecognizer(tap)\n    return view\n  }()\n  \n  private let enterAmountWidget: EnterAmountWidget = {\n    let widget = EnterAmountWidget()\n    widget.translatesAutoresizingMaskIntoConstraints = false\n    widget.addShadowWithRoundedCorners()\n    return widget\n  }()\n  \n  private lazy var ctaButton: UIButton = {\n    let cta = UIButton(type: .system)\n    cta.translatesAutoresizingMaskIntoConstraints = false\n    cta.roundCorners()\n    cta.setTitle(\"충전\", for: .normal)\n    cta.titleLabel?.font = UIFont.systemFont(ofSize: 20, weight: .semibold)\n    cta.setBackgroundImage(UIImage(color: .primaryRed), for: .normal)\n    cta.tintColor = .white\n    cta.addTarget(self, action: #selector(didTapCTAButton), for: .touchUpInside)\n    return cta\n  }()\n  \n  private let activityIndicator: UIActivityIndicatorView = {\n    let activity = UIActivityIndicatorView(style: .medium)\n    activity.translatesAutoresizingMaskIntoConstraints = false\n    activity.hidesWhenStopped = true\n    activity.stopAnimating()\n    return activity\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    title = \"충전하기\"\n    view.backgroundColor = .backgroundColor\n    \n    setupNavigationItem(with: .close, target: self, action: #selector(didTapClose))\n    \n    view.addSubview(selectedPaymentMethodView)\n    view.addSubview(enterAmountWidget)\n    view.addSubview(ctaButton)\n    view.addSubview(activityIndicator)\n    \n    NSLayoutConstraint.activate([\n      selectedPaymentMethodView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      selectedPaymentMethodView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),\n      selectedPaymentMethodView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      selectedPaymentMethodView.heightAnchor.constraint(equalToConstant: 70),\n      \n      enterAmountWidget.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      enterAmountWidget.topAnchor.constraint(equalTo: selectedPaymentMethodView.bottomAnchor, constant: 20),\n      enterAmountWidget.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      ctaButton.heightAnchor.constraint(equalToConstant: 60),\n      ctaButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      ctaButton.topAnchor.constraint(equalTo: enterAmountWidget.bottomAnchor, constant: 40),\n      ctaButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      activityIndicator.centerXAnchor.constraint(equalTo: ctaButton.centerXAnchor),\n      activityIndicator.centerYAnchor.constraint(equalTo: ctaButton.centerYAnchor),\n    ])\n  }\n  \n  @objc\n  private func didTapClose() {\n    listener?.didTapClose()\n  }\n  \n  @objc\n  private func didTapCTAButton() {\n    if let amount = enterAmountWidget.text.flatMap(Double.init) {\n      listener?.didTapTopup(with: amount)\n    }\n  }\n  \n  @objc\n  private func didTapPaymentMethod() {\n    listener?.didTapPaymentMethod()\n  }\n}\n"
  },
  {
    "path": "Samples/Topup/Views/EnterAmountWidget.swift",
    "content": "import UIKit\n\nfinal class EnterAmountWidget: UIView {\n  \n  var text: String? {\n    amountTextField.text\n  }\n\n  init() {\n    super.init(frame: .zero)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    label.text = \"금액\"\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  private lazy var amountStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    let button = UIButton()\n    stackView.axis = .horizontal\n    stackView.alignment = .fill\n    stackView.distribution = .fill\n    stackView.spacing = 5\n    stackView.addArrangedSubview(self.amountTextField)\n    stackView.addArrangedSubview(self.currencyLabel)\n    return stackView\n  }()\n  \n  private let amountTextField: UITextField = {\n    let textField = UITextField()\n    textField.translatesAutoresizingMaskIntoConstraints = false\n    textField.borderStyle = .none\n    textField.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    textField.textAlignment = .right\n    textField.keyboardType = .numberPad\n    return textField\n  }()\n  \n  private let currencyLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    label.text = \"원\"\n    return label\n  }()\n  \n  private func setupViews() {\n    self.backgroundColor = .white\n    self.addSubview(titleLabel)\n    self.addSubview(amountStackView)\n    \n    NSLayoutConstraint.activate([\n      titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 16),\n      titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16),\n      titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),\n      amountStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16),\n      amountStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),\n      amountStackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16),\n      amountStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16)\n    ])\n  }\n}\n\n"
  },
  {
    "path": "Samples/Topup/Views/SelectedPaymentMethodView.swift",
    "content": "import UIKit\n\nstruct SelectedPaymentMethodViewModel {\n  let image: UIImage?\n  let name: String\n  \n  init(_ paymentMethod: PaymentMethod) {\n    image = UIColor(hex: paymentMethod.color).flatMap { UIImage(color: $0) }\n    name = \"\\(paymentMethod.name) \\(paymentMethod.digits)\"\n  }\n}\n\nfinal class SelectedPaymentMethodView: UIView {\n  \n  func update(with viewModel: SelectedPaymentMethodViewModel) {\n    self.thumbnailView.image = viewModel.image\n    self.nameLabel.text = viewModel.name\n  }\n  \n  init() {\n    super.init(frame: .zero)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleToFill\n    imageView.roundCorners(4)\n    imageView.backgroundColor = .systemGray3\n    return imageView\n  }()\n  \n  private let nameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  private let rightChevronIcon: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.image = UIImage(\n      systemName: \"chevron.right\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)\n    )\n    imageView.tintColor = .systemGray3\n    return imageView\n  }()\n  \n  private func setupViews() {\n    self.backgroundColor = .white\n    self.addSubview(thumbnailView)\n    self.addSubview(nameLabel)\n    self.addSubview(rightChevronIcon)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      thumbnailView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      nameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      nameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 22),\n      rightChevronIcon.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      rightChevronIcon.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -24)\n    ])\n  }\n}\n"
  },
  {
    "path": "Samples/TopupDependencyMock.swift",
    "content": "@testable import TopupImp\nimport Foundation\nimport CombineUtil\nimport FinanceEntity\nimport FinanceRepositoryTestSupport\nimport FinanceRepository\nimport Combine\nimport ModernRIBs\nimport RIBsUtil\nimport Topup\nimport SuperUI\n\nfinal class TopupDependencyMock: TopupInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository = CardOnFileRepositoryMock()\n  var paymentMethodStream: CurrentValuePublisher<PaymentMethod> = .init(\n    PaymentMethod(id: \"\", name: \"\", digits: \"\", color: \"\", isPrimary: false)\n  )\n}\n\nfinal class TopupRoutingMock: TopupRouting {\n  \n  var attachAddPaymentMethodCallCount = 0\n  var attachAddPaymentMethodCloseButtonType: DismissButtonType?\n  func attachAddPaymentMethod(closeButtonType: DismissButtonType) {\n    attachAddPaymentMethodCallCount += 1\n    attachAddPaymentMethodCloseButtonType = closeButtonType\n  }\n  \n  var detachAddPaymentMethodCallCount = 0\n  func detachAddPaymentMethod() {\n    detachAddPaymentMethodCallCount += 1\n  }\n  \n  var attachEnterAmountCallCount = 0\n  func attachEnterAmount() {\n    attachEnterAmountCallCount += 1\n  }\n  \n  var detachEnterAmountCallCount = 0\n  func detachEnterAmount() {\n    detachEnterAmountCallCount += 1\n  }\n  \n  var attachCardOnFileCallCount = 0\n  var attachCardOnFileCallCountPaymentMethods: [PaymentMethod]?\n  func attachCardOnFile(paymentMethods: [PaymentMethod]) {\n    attachCardOnFileCallCount += 1\n  }\n  \n  var detachCardOnFileCallCount = 0\n  func detachCardOnFile() {\n    detachCardOnFileCallCount += 1\n  }\n  \n  var popToRootCallCount = 0\n  func popToRoot() {\n    popToRootCallCount += 1\n  }\n  \n  // Variables\n  var interactable: Interactable { didSet { interactableSetCallCount += 1 } }\n  var interactableSetCallCount = 0\n  var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  var childrenSetCallCount = 0\n  var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  var lifecycleSubjectSetCallCount = 0\n  var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n  \n  // Function Handlers\n  var loadHandler: (() -> ())?\n  var loadCallCount: Int = 0\n  var attachChildHandler: ((_ child: Routing) -> ())?\n  var attachChildCallCount: Int = 0\n  var detachChildHandler: ((_ child: Routing) -> ())?\n  var detachChildCallCount: Int = 0\n  \n  init(\n    interactable: Interactable\n  ) {\n    self.interactable = interactable\n  }\n  \n  var cleanupViewsCallCount = 0\n  func cleanupViews() {\n    cleanupViewsCallCount += 1\n  }\n  \n  func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n  \n  func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n  \n  func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n}\n\nfinal class TopupInteractableMock: TopupInteractable {\n  var router: TopupRouting?\n  var listener: TopupListener?\n  var presentationDelegateProxy = AdaptivePresentationControllerDelegateProxy()\n  \n  var addPaymentMethodDidTapCloseCallCount = 0\n  func addPaymentMethodDidTapClose() {\n    addPaymentMethodDidTapCloseCallCount += 1\n  }\n  \n  var addPaymentMethodDidAddCardCallCount = 0\n  var addPaymentMethodDidAddCardPaymentMethod: PaymentMethod?\n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    addPaymentMethodDidAddCardCallCount += 1\n    addPaymentMethodDidAddCardPaymentMethod = paymentMethod\n  }\n  \n  var enterAmountDidTapCloseCallCount = 0\n  func enterAmountDidTapClose() {\n    enterAmountDidTapCloseCallCount += 1\n  }\n  \n  var enterAmountDidTapPaymentMethodCallCount = 0\n  func enterAmountDidTapPaymentMethod() {\n    enterAmountDidTapPaymentMethodCallCount += 1\n  }\n  \n  var enterAmountDidFinishTopupCallCount = 0\n  func enterAmountDidFinishTopup() {\n    enterAmountDidFinishTopupCallCount += 1\n  }\n  \n  var cardOnFileDidTapCloseCallCount = 0\n  func cardOnFileDidTapClose() {\n    cardOnFileDidTapCloseCallCount += 1\n  }\n  \n  var cardOnFileDidTapAddCardCallCount = 0\n  func cardOnFileDidTapAddCard() {\n    cardOnFileDidTapAddCardCallCount += 1\n  }\n  \n  var cardOnFileDidSelectCardCallCount = 0\n  var cardOnFileDidSelectCardIndex: Int?\n  func cardOnFileDidSelectCard(at index: Int) {\n    cardOnFileDidSelectCardCallCount += 1\n    cardOnFileDidSelectCardIndex = index\n  }\n  \n  func activate() {\n    \n  }\n  \n  func deactivate() {\n    \n  }\n  \n  var isActive: Bool { isActiveSubject.value }\n  var isActiveStream: AnyPublisher<Bool, Never> { isActiveSubject.eraseToAnyPublisher() }\n  private let isActiveSubject = CurrentValueSubject<Bool, Never>(false)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/.gitignore",
    "content": "xcuserdata/\n"
  },
  {
    "path": "completed/MiniSuperApp/AddPaymentMethodIntegrationTests/AddPaymentMethodIntegrationTests.swift",
    "content": "import XCTest\nimport Hammer\nimport FinanceRepository\nimport FinanceRepositoryTestSupport\nimport AddPaymentMethodTestSupport\nimport ModernRIBs\nimport RIBsUtil\nimport FinanceEntity\n@testable import AddPaymentMethodImp\n\nclass AddPaymentMethodIntegrationTests: XCTestCase {\n  \n  private var eventGenerator: EventGenerator!\n  private var dependency: AddPaymentMethodDependencyMock!\n  private var listener: AddPaymentMethodListenerMock!\n  private var viewController: UIViewController!\n  private var router: Routing!\n  \n  private var repository: CardOnFileRepositoryMock {\n    dependency.cardOnFileRepository as! CardOnFileRepositoryMock\n  }\n  \n  override func setUpWithError() throws {\n    try super.setUpWithError()\n    \n    self.dependency = AddPaymentMethodDependencyMock()\n    self.listener = AddPaymentMethodListenerMock()\n    \n    let builder = AddPaymentMethodBuilder(dependency: self.dependency)\n    let router = builder.build(withListener: self.listener, closeButtonType: .close)\n    \n    let navigation = NavigationControllerable(root: router.viewControllable)\n    self.viewController = navigation.uiviewController\n    \n    eventGenerator = try EventGenerator(viewController: navigation.navigationController)\n    \n    router.load()\n    router.interactable.activate()\n    \n    self.router = router\n  }\n  \n  func testAddPaymentMethod() throws {\n    // given\n    repository.addedPaymentMethod = PaymentMethod(\n      id: \"1234\",\n      name: \"\",\n      digits: \"\",\n      color: \"\",\n      isPrimary: false\n    )\n    \n    let cardNumberTF = try eventGenerator.viewWithIdentifier(\"addpaymentmethod_cardnumber_textfield\")\n    try eventGenerator.fingerTap(at: cardNumberTF)\n    try eventGenerator.keyType(\"1234123412341234\")\n    \n    let cvc = try eventGenerator.viewWithIdentifier(\"addpaymentmethod_security_textfield\")\n    try eventGenerator.fingerTap(at: cvc)\n    try eventGenerator.keyType(\"123\")\n    \n    let expiry = try eventGenerator.viewWithIdentifier(\"addpaymentmethod_expiry_textfield\")\n    try eventGenerator.fingerTap(at: expiry)\n    try eventGenerator.keyType(\"1212\")\n    \n    // when\n    let confirm = try eventGenerator.viewWithIdentifier(\"addpaymentmethod_addcard_button\")\n    try eventGenerator.fingerTap(at: confirm)\n    \n    // then\n    XCTAssertEqual(repository.addCardCallCount, 1)\n    try eventGenerator.wait(0.2)\n    XCTAssertEqual(listener.addPaymentMethodDidAddCardCallCount, 1)\n    XCTAssertEqual(listener.addPaymentMethodDidAddCardPaymentMethod?.id, \"1234\")\n  }\n}\n\nfinal class AddPaymentMethodDependencyMock: AddPaymentMethodDependency {\n  var cardOnFileRepository: CardOnFileRepository = CardOnFileRepositoryMock()\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"CX\",\n  platforms: [.iOS(.v14)],\n  products: [\n    // Products define the executables and libraries a package produces, and make them visible to other packages.\n    .library(\n      name: \"AppHome\",\n      targets: [\"AppHome\"]\n    ),\n  ],\n  dependencies: [\n    .package(name: \"ModernRIBs\", url: \"https://github.com/DevYeom/ModernRIBs\", .exact(\"1.0.1\")),\n    .package(path: \"../Finance\"),\n    .package(path: \"../Transport\"),\n    .package(path: \"../Platform\")\n  ],\n  targets: [\n    // Targets are the basic building blocks of a package. A target can define a module or a test suite.\n    // Targets can depend on other targets in this package, and on products in packages this package depends on.\n    .target(\n      name: \"AppHome\",\n      dependencies: [\n        \"ModernRIBs\",\n        .product(name: \"FinanceRepository\", package: \"Finance\"),\n        .product(name: \"TransportHome\", package: \"Transport\"),\n        .product(name: \"SuperUI\", package: \"Platform\"),\n      ]\n    ),\n  ]\n)\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/README.md",
    "content": "# CX\n\nA description of this package.\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/AppHomeBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\nimport TransportHome\n\npublic protocol AppHomeDependency: Dependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n  var superPayRepository: SuperPayRepository { get }\n  var transportHomeBuildable: TransportHomeBuildable { get }\n}\n\nfinal class AppHomeComponent: Component<AppHomeDependency> {\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n  var superPayRepository: SuperPayRepository { dependency.superPayRepository }\n  var transportHomeBuildable: TransportHomeBuildable { dependency.transportHomeBuildable }\n}\n\n// MARK: - Builder\n\npublic protocol AppHomeBuildable: Buildable {\n  func build(withListener listener: AppHomeListener) -> ViewableRouting\n}\n\npublic final class AppHomeBuilder: Builder<AppHomeDependency>, AppHomeBuildable {\n  \n  public override init(dependency: AppHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: AppHomeListener) -> ViewableRouting {\n    let component = AppHomeComponent(dependency: dependency)\n    let viewController = AppHomeViewController()\n    let interactor = AppHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    \n    return AppHomeRouter(\n      interactor: interactor,\n      viewController: viewController,\n      transportHomeBuildable: component.transportHomeBuildable\n    )\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/AppHomeInteractor.swift",
    "content": "import ModernRIBs\n\nprotocol AppHomeRouting: ViewableRouting {\n  func attachTransportHome()\n  func detachTransportHome()\n}\n\nprotocol AppHomePresentable: Presentable {\n  var listener: AppHomePresentableListener? { get set }\n  \n  func updateWidget(_ viewModels: [HomeWidgetViewModel])\n}\n\npublic protocol AppHomeListener: AnyObject {\n}\n\nfinal class AppHomeInteractor: PresentableInteractor<AppHomePresentable>, AppHomeInteractable, AppHomePresentableListener {\n  \n  weak var router: AppHomeRouting?\n  weak var listener: AppHomeListener?\n  \n  override init(presenter: AppHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    let viewModels = [\n      HomeWidgetModel(\n        imageName: \"car\",\n        title: \"슈퍼택시\",\n        tapHandler: { [weak self] in\n          self?.router?.attachTransportHome()\n        }\n      ),\n      HomeWidgetModel(\n        imageName: \"cart\",\n        title: \"슈퍼마트\",\n        tapHandler: { }\n      )\n    ]\n    \n    presenter.updateWidget(viewModels.map(HomeWidgetViewModel.init))\n  }\n  \n  func transportHomeDidTapClose() {\n    router?.detachTransportHome()\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/AppHomeRouter.swift",
    "content": "import ModernRIBs\nimport SuperUI\nimport TransportHome\n\nprotocol AppHomeInteractable: Interactable, TransportHomeListener {\n  var router: AppHomeRouting? { get set }\n  var listener: AppHomeListener? { get set }\n}\n\nprotocol AppHomeViewControllable: ViewControllable {\n\n}\n\nfinal class AppHomeRouter: ViewableRouter<AppHomeInteractable, AppHomeViewControllable>, AppHomeRouting {\n  \n  private let transportHomeBuildable: TransportHomeBuildable\n  private var transportHomeRouting: Routing?\n  private let transitioningDelegate: PushModalPresentationController\n  \n  init(\n    interactor: AppHomeInteractable,\n    viewController: AppHomeViewControllable,\n    transportHomeBuildable: TransportHomeBuildable\n  ) {\n    self.transitioningDelegate = PushModalPresentationController()\n    self.transportHomeBuildable = transportHomeBuildable\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachTransportHome() {\n    if transportHomeRouting != nil {\n      return\n    }\n    \n    let router = transportHomeBuildable.build(withListener: interactor)\n    presentWithPushTransition(router.viewControllable, animated: true)\n    attachChild(router)\n    self.transportHomeRouting = router\n  }\n  \n  func detachTransportHome() {\n    guard let router = transportHomeRouting else {\n      return\n    }\n    \n    viewController.dismiss(completion: nil)\n    self.transportHomeRouting = nil\n    detachChild(router)\n  }\n  \n  private func presentWithPushTransition(_ viewControllable: ViewControllable, animated: Bool) {\n    viewControllable.uiviewController.modalPresentationStyle = .custom\n    viewControllable.uiviewController.transitioningDelegate = transitioningDelegate\n    viewController.present(viewControllable, animated: true, completion: nil)\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/AppHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol AppHomePresentableListener: AnyObject {\n}\n\nfinal class AppHomeViewController: UIViewController, AppHomePresentable, AppHomeViewControllable {\n  \n  weak var listener: AppHomePresentableListener?\n  \n  private let widgetStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.axis = .horizontal\n    stackView.distribution = .fillEqually\n    stackView.alignment = .top\n    stackView.spacing = 20\n    return stackView\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  func updateWidget(_ viewModels: [HomeWidgetViewModel]) {\n    let views = viewModels.map { HomeWidgetView(viewModel: $0) }\n    \n    views.forEach {\n      $0.addShadowWithRoundedCorners(12)\n      widgetStackView.addArrangedSubview($0)\n    }\n  }\n  \n  private func setupViews() {\n    title = \"홈\"\n    tabBarItem = UITabBarItem(title: \"홈\", image: UIImage(systemName: \"house\"), selectedImage: UIImage(systemName: \"house.fill\"))\n    view.backgroundColor = .backgroundColor\n    view.addSubview(widgetStackView)\n    \n    NSLayoutConstraint.activate([\n      widgetStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),\n      widgetStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      widgetStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)\n    ])\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/HomeWidgetModel.swift",
    "content": "import Foundation\n\nstruct HomeWidgetModel {\n  let imageName: String\n  let title: String\n  let tapHandler: () -> Void\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/CX/Sources/AppHome/Views/HomeWidgetView.swift",
    "content": "import UIKit\n\nstruct HomeWidgetViewModel {\n  let image: UIImage?\n  let title: String\n  let tapHandler: () -> Void\n  \n  init(_ model: HomeWidgetModel) {\n    image = UIImage(systemName: model.imageName)\n    title = model.title\n    tapHandler = model.tapHandler\n  }\n}\n\nfinal class HomeWidgetView: UIView {\n  \n  init(viewModel: HomeWidgetViewModel) {\n    super.init(frame: .zero)\n    \n    setupViews()\n    update(with: viewModel)\n  }\n  \n  required init?(coder: NSCoder) {\n    fatalError()\n  }\n  \n  private var tapHandler: (() -> Void)?\n  \n  private func update(with viewModel: HomeWidgetViewModel) {\n    imageView.image = viewModel.image\n    titleLabel.text = viewModel.title\n    tapHandler = viewModel.tapHandler\n  }\n\n  private let imageView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.tintColor = .black\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    return imageView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.textAlignment = .center\n    label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)\n    return label\n  }()\n  \n  private func setupViews() {\n    addSubview(imageView)\n    addSubview(titleLabel)\n    backgroundColor = .white\n    \n    let tap = UITapGestureRecognizer(target: self, action: #selector(didTap))\n    addGestureRecognizer(tap)\n    \n    NSLayoutConstraint.activate([\n      imageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 15),\n      imageView.centerXAnchor.constraint(equalTo: self.centerXAnchor),\n      imageView.widthAnchor.constraint(equalToConstant: 50),\n      imageView.heightAnchor.constraint(equalToConstant: 50),\n      \n      titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5),\n      titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor),\n      titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -14)\n    ])\n  }\n  \n  @objc\n  private func didTap() {\n    tapHandler?()\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/.swiftpm/xcode/xcshareddata/xcschemes/TopupImp.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"TopupImp\"\n               BuildableName = \"TopupImp\"\n               BlueprintName = \"TopupImp\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"TopupImpTests\"\n               BuildableName = \"TopupImpTests\"\n               BlueprintName = \"TopupImpTests\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"TopupImp\"\n            BuildableName = \"TopupImp\"\n            BlueprintName = \"TopupImp\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/.swiftpm/xcode/xcshareddata/xcschemes/TopupImpTests.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"TopupImpTests\"\n               BuildableName = \"TopupImpTests\"\n               BlueprintName = \"TopupImpTests\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"Finance\",\n  platforms: [.iOS(.v14)],\n  products: [\n    .library(\n      name: \"AddPaymentMethod\",\n      targets: [\"AddPaymentMethod\"]\n    ),\n    .library(\n      name: \"AddPaymentMethodImp\",\n      targets: [\"AddPaymentMethodImp\"]\n    ),\n    .library(\n      name: \"AddPaymentMethodTestSupport\",\n      targets: [\"AddPaymentMethodTestSupport\"]\n    ),\n    .library(\n      name: \"Topup\",\n      targets: [\"Topup\"]\n    ),\n    .library(\n      name: \"TopupImp\",\n      targets: [\"TopupImp\"]\n    ),\n    .library(\n      name: \"TopupTestSupport\",\n      targets: [\"TopupTestSupport\"]\n    ),\n    .library(\n      name: \"FinanceHome\",\n      targets: [\"FinanceHome\"]\n    ),\n    .library(\n      name: \"FinanceEntity\",\n      targets: [\"FinanceEntity\"]\n    ),\n    .library(\n      name: \"FinanceRepository\",\n      targets: [\"FinanceRepository\"]\n    ),\n    .library(\n      name: \"FinanceRepositoryTestSupport\",\n      targets: [\"FinanceRepositoryTestSupport\"]\n    ),\n  ],\n  dependencies: [\n    .package(name: \"ModernRIBs\", url: \"https://github.com/DevYeom/ModernRIBs\", .exact(\"1.0.1\")),\n    .package(path: \"../Platform\")\n  ],\n  targets: [\n    .target(\n      name: \"AddPaymentMethod\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"FinanceEntity\",\n        .product(name: \"RIBsUtil\", package: \"Platform\"),\n      ]\n    ),\n    .target(\n      name: \"AddPaymentMethodImp\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"AddPaymentMethod\",\n        \"FinanceEntity\",\n        \"FinanceRepository\",\n        .product(name: \"RIBsUtil\", package: \"Platform\"),\n        .product(name: \"SuperUI\", package: \"Platform\")\n      ]\n    ),\n    .target(\n      name: \"AddPaymentMethodTestSupport\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"FinanceEntity\",\n        \"AddPaymentMethod\",\n        .product(name: \"RIBsUtil\", package: \"Platform\"),\n        .product(name: \"RIBsTestSupport\", package: \"Platform\"),\n      ]\n    ),\n    .target(\n      name: \"Topup\",\n      dependencies: [\n        \"ModernRIBs\"\n      ]\n    ),\n    .target(\n      name: \"TopupImp\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"Topup\",\n        \"FinanceEntity\",\n        \"FinanceRepository\",\n        \"AddPaymentMethod\",\n        .product(name: \"RIBsUtil\", package: \"Platform\"),\n        .product(name: \"SuperUI\", package: \"Platform\")\n      ]\n    ),\n    .target(\n      name: \"TopupTestSupport\",\n      dependencies: [\n        \"Topup\"\n      ]\n    ),\n    .target(\n      name: \"FinanceHome\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"FinanceEntity\",\n        \"FinanceRepository\",\n        \"AddPaymentMethod\",\n        \"Topup\",\n        .product(name: \"RIBsUtil\", package: \"Platform\"),\n        .product(name: \"SuperUI\", package: \"Platform\")\n      ]\n    ),\n    .target(\n      name: \"FinanceEntity\",\n      dependencies: [\n      ]\n    ),\n    .target(\n      name: \"FinanceRepository\",\n      dependencies: [\n        \"FinanceEntity\",\n        .product(name: \"CombineUtil\", package: \"Platform\"),\n        .product(name: \"Network\", package: \"Platform\")\n      ]\n    ),\n    .target(\n      name: \"FinanceRepositoryTestSupport\",\n      dependencies: [\n        \"FinanceEntity\",\n        \"FinanceRepository\",\n        .product(name: \"CombineUtil\", package: \"Platform\")\n      ]\n    ),\n    .testTarget(\n      name: \"TopupImpTests\",\n      dependencies: [\n        \"TopupImp\",\n        \"FinanceRepositoryTestSupport\",\n        \"TopupTestSupport\",\n        \"AddPaymentMethodTestSupport\",\n        .product(name: \"RIBsTestSupport\", package: \"Platform\"),\n        .product(name: \"PlatformTestSupport\", package: \"Platform\")\n      ],\n      exclude: [\n        \"EnterAmount/__Snapshots__\",\n        \"CardOnFile/__Snapshots__\"\n      ]\n    )\n  ]\n)\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/README.md",
    "content": "# Finance\n\nA description of this package.\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethod/AddPaymentMethodInterface.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport FinanceEntity\nimport RIBsUtil\n\npublic protocol AddPaymentMethodBuildable: Buildable {\n  func build(withListener listener: AddPaymentMethodListener, closeButtonType: DismissButtonType) -> ViewableRouting\n}\n\npublic protocol AddPaymentMethodListener: AnyObject {\n  func addPaymentMethodDidTapClose()\n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethodImp/AddPaymentMethodBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\nimport RIBsUtil\nimport AddPaymentMethod\n\npublic protocol AddPaymentMethodDependency: Dependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n}\n\nfinal class AddPaymentMethodComponent: Component<AddPaymentMethodDependency>, AddPaymentMethodInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n}\n\n// MARK: - Builder\n\npublic final class AddPaymentMethodBuilder: Builder<AddPaymentMethodDependency>, AddPaymentMethodBuildable {\n  \n  public override init(dependency: AddPaymentMethodDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: AddPaymentMethodListener, closeButtonType: DismissButtonType) -> ViewableRouting {\n    let component = AddPaymentMethodComponent(dependency: dependency)\n    let viewController = AddPaymentMethodViewController(closeButtonType: closeButtonType)\n    let interactor = AddPaymentMethodInteractor(\n      presenter: viewController,\n      dependency: component\n    )\n    interactor.listener = listener\n    return AddPaymentMethodRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethodImp/AddPaymentMethodInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport FinanceEntity\nimport FinanceRepository\nimport AddPaymentMethod\nimport Foundation\n\nprotocol AddPaymentMethodRouting: ViewableRouting {\n}\n\nprotocol AddPaymentMethodPresentable: Presentable {\n  var listener: AddPaymentMethodPresentableListener? { get set }\n}\n\nprotocol AddPaymentMethodInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n}\n\nfinal class AddPaymentMethodInteractor: PresentableInteractor<AddPaymentMethodPresentable>, AddPaymentMethodInteractable, AddPaymentMethodPresentableListener {\n  \n  weak var router: AddPaymentMethodRouting?\n  weak var listener: AddPaymentMethodListener?\n  \n  private let dependency: AddPaymentMethodInteractorDependency\n  \n  private var cancellables: Set<AnyCancellable>\n  \n  init(\n    presenter: AddPaymentMethodPresentable,\n    dependency: AddPaymentMethodInteractorDependency\n  ) {\n    self.dependency = dependency\n    self.cancellables = .init()\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n  \n  func didTapClose() {\n    listener?.addPaymentMethodDidTapClose()\n  }\n  \n  func didTapConfirm(with number: String, cvc: String, expiry: String) {\n    let info = AddPaymentMethodInfo(number: number, cvc: cvc, expiration: expiry)\n    dependency.cardOnFileRepository.addCard(info: info)\n      .receive(on: DispatchQueue.main)\n      .sink(\n        receiveCompletion: { _ in },\n        receiveValue: { [weak self] method in\n          self?.listener?.addPaymentMethodDidAddCard(paymentMethod: method)\n        }\n      ).store(in: &cancellables)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethodImp/AddPaymentMethodRouter.swift",
    "content": "import ModernRIBs\nimport AddPaymentMethod\n\nprotocol AddPaymentMethodInteractable: Interactable {\n  var router: AddPaymentMethodRouting? { get set }\n  var listener: AddPaymentMethodListener? { get set }\n}\n\nprotocol AddPaymentMethodViewControllable: ViewControllable {\n}\n\nfinal class AddPaymentMethodRouter: ViewableRouter<AddPaymentMethodInteractable, AddPaymentMethodViewControllable>, AddPaymentMethodRouting {\n  \n  override init(interactor: AddPaymentMethodInteractable, viewController: AddPaymentMethodViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethodImp/AddPaymentMethodViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\nimport RIBsUtil\nimport SuperUI\n\nprotocol AddPaymentMethodPresentableListener: AnyObject {\n  func didTapClose()\n  func didTapConfirm(with number: String, cvc: String, expiry: String)\n}\n\nfinal class AddPaymentMethodViewController: UIViewController, AddPaymentMethodPresentable, AddPaymentMethodViewControllable {\n  \n  weak var listener: AddPaymentMethodPresentableListener?\n  \n  private let cardNumberTextField: UITextField = {\n    let textField = makeTextField()\n    textField.placeholder = \"카드 번호\"\n    textField.accessibilityIdentifier = \"addpaymentmethod_cardnumber_textfield\"\n    return textField\n  }()\n  \n  private let stackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.axis = .horizontal\n    stackView.alignment = .center\n    stackView.distribution = .fillEqually\n    stackView.spacing = 14\n    return stackView\n  }()\n  \n  private let securityTextField: UITextField = {\n    let textField = makeTextField()\n    textField.placeholder = \"CVC\"\n    textField.accessibilityIdentifier = \"addpaymentmethod_security_textfield\"\n    return textField\n  }()\n  \n  private let expirationTextField: UITextField = {\n    let textField = makeTextField()\n    textField.placeholder = \"유효기한\"\n    textField.accessibilityIdentifier = \"addpaymentmethod_expiry_textfield\"\n    return textField\n  }()\n  \n  private lazy var addCardButton: UIButton = {\n    let button = UIButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.roundCorners()\n    button.backgroundColor = .primaryRed\n    button.setTitle(\"추가하기\", for: .normal)\n    button.accessibilityIdentifier = \"addpaymentmethod_addcard_button\"\n    button.addTarget(self, action: #selector(didTapAddCard), for: .touchUpInside)\n    return button\n  }()\n  \n  private static func makeTextField() -> UITextField {\n    let textField = UITextField()\n    textField.translatesAutoresizingMaskIntoConstraints = false\n    textField.backgroundColor = .white\n    textField.borderStyle = .roundedRect\n    textField.keyboardType = .numberPad\n    return textField\n  }\n  \n  init(closeButtonType: DismissButtonType) {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n    setupNavigationItem(with: closeButtonType, target: self, action: #selector(didTapClose))\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n    setupNavigationItem(with: .close, target: self, action: #selector(didTapClose))\n  }\n  \n  private func setupViews() {\n    title = \"카드 추가\"\n    \n    view.backgroundColor = .backgroundColor\n    view.addSubview(cardNumberTextField)\n    view.addSubview(stackView)\n    view.addSubview(addCardButton)\n    \n    stackView.addArrangedSubview(securityTextField)\n    stackView.addArrangedSubview(expirationTextField)\n    \n    NSLayoutConstraint.activate([\n      cardNumberTextField.topAnchor.constraint(equalTo: view.topAnchor, constant: 40),\n      cardNumberTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40),\n      cardNumberTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40),\n      \n      cardNumberTextField.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -20),\n      stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40),\n      stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40),\n      \n      stackView.bottomAnchor.constraint(equalTo: addCardButton.topAnchor, constant: -20),\n      addCardButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40),\n      addCardButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40),\n      \n      cardNumberTextField.heightAnchor.constraint(equalToConstant: 60),\n      securityTextField.heightAnchor.constraint(equalToConstant: 60),\n      expirationTextField.heightAnchor.constraint(equalToConstant: 60),\n      addCardButton.heightAnchor.constraint(equalToConstant: 60)\n    ])\n  }\n  \n  @objc\n  private func didTapAddCard() {\n    if\n      let number = cardNumberTextField.text,\n      let cvc = securityTextField.text,\n      let expiry = expirationTextField.text {\n      listener?.didTapConfirm(with: number, cvc: cvc, expiry: expiry)\n    }\n  }\n  \n  @objc\n  private func didTapClose() {\n    listener?.didTapClose()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/AddPaymentMethodTestSupport/AddPaymentMethodTestSupport.swift",
    "content": "import Foundation\nimport AddPaymentMethod\nimport ModernRIBs\nimport RIBsUtil\nimport RIBsTestSupport\nimport FinanceEntity\n\npublic final class AddPaymentMethodBuildableMock: AddPaymentMethodBuildable {\n  \n  public var buildCallCount = 0\n  public var closeButtonType: DismissButtonType?\n  public func build(withListener listener: AddPaymentMethodListener, closeButtonType: DismissButtonType) -> ViewableRouting {\n    buildCallCount += 1\n    self.closeButtonType = closeButtonType\n    \n    return ViewableRoutingMock(\n      interactable: Interactor(),\n      viewControllable: ViewControllableMock()\n    )\n  }\n  \n  public init() {\n    \n  }\n}\n\npublic final class AddPaymentMethodListenerMock: AddPaymentMethodListener {\n  public var addPaymentMethodDidTapCloseCallCount = 0\n  public func addPaymentMethodDidTapClose() {\n    addPaymentMethodDidTapCloseCallCount += 1\n  }\n  \n  public var addPaymentMethodDidAddCardCallCount = 0\n  public var addPaymentMethodDidAddCardPaymentMethod: PaymentMethod?\n  public func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    addPaymentMethodDidAddCardCallCount += 1\n    addPaymentMethodDidAddCardPaymentMethod = paymentMethod\n  }\n  \n  public init() {\n    \n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceEntity/AddPaymentMethodInfo.swift",
    "content": "import Foundation\n\npublic struct AddPaymentMethodInfo {\n  public let number: String\n  public let cvc: String\n  public let expiration: String\n  \n  public init(\n    number: String,\n    cvc: String,\n    expiration: String\n  ) {\n    self.number = number\n    self.cvc = cvc\n    self.expiration = expiration\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceEntity/PaymentMethod.swift",
    "content": "import Foundation\n\npublic struct PaymentMethod: Decodable {\n  public let id: String\n  public let name: String\n  public let digits: String\n  public let color: String\n  public let isPrimary: Bool\n  \n  public init(\n    id: String,\n    name: String,\n    digits: String,\n    color: String,\n    isPrimary: Bool\n  ) {\n    self.id = id\n    self.name = name\n    self.digits = digits\n    self.color = color\n    self.isPrimary = isPrimary\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/CardOnFileDashboardBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\n\nprotocol CardOnFileDashboardDependency: Dependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n}\n\nfinal class CardOnFileDashboardComponent: Component<CardOnFileDashboardDependency>, CardOnFileDashboardInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n}\n\n// MARK: - Builder\n\nprotocol CardOnFileDashboardBuildable: Buildable {\n  func build(withListener listener: CardOnFileDashboardListener) -> CardOnFileDashboardRouting\n}\n\nfinal class CardOnFileDashboardBuilder: Builder<CardOnFileDashboardDependency>, CardOnFileDashboardBuildable {\n  \n  override init(dependency: CardOnFileDashboardDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: CardOnFileDashboardListener) -> CardOnFileDashboardRouting {\n    let component = CardOnFileDashboardComponent(dependency: dependency)\n    let viewController = CardOnFileDashboardViewController()\n    let interactor = CardOnFileDashboardInteractor(\n      presenter: viewController,\n      dependency: component\n    )\n    interactor.listener = listener\n    return CardOnFileDashboardRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/CardOnFileDashboardInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport FinanceRepository\nimport Foundation\n\nprotocol CardOnFileDashboardRouting: ViewableRouting {\n}\n\nprotocol CardOnFileDashboardPresentable: Presentable {\n  var listener: CardOnFileDashboardPresentableListener? { get set }\n  \n  func update(with viewModels: [PaymentMethodViewModel])\n}\n\nprotocol CardOnFileDashboardListener: AnyObject {\n  func cardOnFileDashboardDidTapAddPaymentMethod()\n}\n\nprotocol CardOnFileDashboardInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n}\n\nfinal class CardOnFileDashboardInteractor: PresentableInteractor<CardOnFileDashboardPresentable>, CardOnFileDashboardInteractable, CardOnFileDashboardPresentableListener {\n  \n  weak var router: CardOnFileDashboardRouting?\n  weak var listener: CardOnFileDashboardListener?\n  \n  private let dependency: CardOnFileDashboardInteractorDependency\n  \n  private var cancellables: Set<AnyCancellable>\n  \n  init(\n    presenter: CardOnFileDashboardPresentable,\n    dependency: CardOnFileDashboardInteractorDependency\n  ) {\n    self.dependency = dependency\n    self.cancellables = .init()\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    dependency.cardOnFileRepository.cardOnFile\n      .receive(on: DispatchQueue.main)\n      .sink { methods in\n        let viewModels = methods.prefix(5).map(PaymentMethodViewModel.init)\n        self.presenter.update(with: viewModels)\n      }.store(in: &cancellables)\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    \n    cancellables.forEach { $0.cancel() }\n    cancellables.removeAll()\n  }\n  \n  func didTapAddPaymentMethod() {\n    listener?.cardOnFileDashboardDidTapAddPaymentMethod()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/CardOnFileDashboardRouter.swift",
    "content": "import ModernRIBs\n\nprotocol CardOnFileDashboardInteractable: Interactable {\n  var router: CardOnFileDashboardRouting? { get set }\n  var listener: CardOnFileDashboardListener? { get set }\n}\n\nprotocol CardOnFileDashboardViewControllable: ViewControllable {\n}\n\nfinal class CardOnFileDashboardRouter: ViewableRouter<CardOnFileDashboardInteractable, CardOnFileDashboardViewControllable>, CardOnFileDashboardRouting {\n  \n  override init(interactor: CardOnFileDashboardInteractable, viewController: CardOnFileDashboardViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/CardOnFileDashboardViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol CardOnFileDashboardPresentableListener: AnyObject {\n  func didTapAddPaymentMethod()\n}\n\nfinal class CardOnFileDashboardViewController: UIViewController, CardOnFileDashboardPresentable, CardOnFileDashboardViewControllable {\n  \n  weak var listener: CardOnFileDashboardPresentableListener?\n  \n  private let headerStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.alignment = .fill\n    stackView.distribution = .equalSpacing\n    stackView.axis = .horizontal\n    return stackView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 22, weight: .semibold)\n    label.text = \"카드 및 계좌\"\n    return label\n  }()\n  \n  private lazy var seeAllButton: UIButton = {\n    let button = UIButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.setTitle(\"전체보기\", for: .normal)\n    button.setTitleColor(.systemBlue, for: .normal)\n    button.addTarget(self, action: #selector(seeAllButtonTapped), for: .touchUpInside)\n    return button\n  }()\n  \n  private let cardOnFileStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.alignment = .fill\n    stackView.distribution = .equalSpacing\n    stackView.axis = .vertical\n    stackView.spacing = 12\n    return stackView\n  }()\n  \n  private lazy var addMethodButton: AddPaymentMethodButton = {\n    let button = AddPaymentMethodButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.roundCorners()\n    button.backgroundColor = .systemGray4\n    button.addTarget(self, action: #selector(addButtonDidTap), for: .touchUpInside)\n    return button\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  func update(with viewModels: [PaymentMethodViewModel]) {\n    cardOnFileStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }\n    \n    let views = viewModels.map(PaymentMethodView.init)\n    \n    views.forEach {\n      $0.roundCorners()\n      cardOnFileStackView.addArrangedSubview($0)\n    }\n    \n    cardOnFileStackView.addArrangedSubview(addMethodButton)\n    \n    let heightConstraints = views.map { $0.heightAnchor.constraint(equalToConstant: 60) }\n    NSLayoutConstraint.activate(heightConstraints)\n  }\n  \n  private func setupViews() {\n    view.addSubview(headerStackView)\n    view.addSubview(cardOnFileStackView)\n    \n    headerStackView.addArrangedSubview(titleLabel)\n    headerStackView.addArrangedSubview(seeAllButton)\n    \n    NSLayoutConstraint.activate([\n      headerStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),\n      headerStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      headerStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      cardOnFileStackView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 10),\n      cardOnFileStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      cardOnFileStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      cardOnFileStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n      \n      addMethodButton.heightAnchor.constraint(equalToConstant: 60),\n    ])\n  }\n  \n  @objc\n  private func seeAllButtonTapped() {\n    \n  }\n  \n  @objc\n  private func addButtonDidTap() {\n    listener?.didTapAddPaymentMethod()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/PaymentMethodViewModel.swift",
    "content": "import UIKit\nimport FinanceEntity\n\nstruct PaymentMethodViewModel {\n  let name: String\n  let digits: String\n  let color: UIColor\n  \n  init(_ paymentMethod: PaymentMethod) {\n    name = paymentMethod.name\n    digits = \"**** \\(paymentMethod.digits)\"\n    color = UIColor(hex: paymentMethod.color) ?? .systemGray2\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/Views/AddPaymentMethodButton.swift",
    "content": "import UIKit\n\nfinal class AddPaymentMethodButton: UIControl {\n  \n  private let plusIcon: UIImageView = {\n    let imageView = UIImageView(\n      image: UIImage(\n        systemName: \"plus\",\n        withConfiguration: UIImage.SymbolConfiguration(pointSize: 24, weight: .semibold)\n      )\n    )\n    imageView.tintColor = .white\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    \n    return imageView\n  }()\n  \n  init() {\n    super.init(frame: .zero)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(plusIcon)\n    \n    NSLayoutConstraint.activate([\n      plusIcon.centerXAnchor.constraint(equalTo: self.centerXAnchor),\n      plusIcon.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n    ])\n  }\n  \n}\n\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/CardOnFileDashboard/Views/PaymentMethodView.swift",
    "content": "import UIKit\n\nfinal class PaymentMethodView: UIView {\n  \n  private let nameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 18, weight: .semibold)\n    label.textColor = .white\n    label.text = \"우리은행\"\n    return label\n  }()\n  \n  private let subtitleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 15, weight: .regular)\n    label.textColor = .white\n    label.text = \"**** 9999\"\n    return label\n  }()\n  \n  init(viewModel: PaymentMethodViewModel) {\n    super.init(frame: .zero)\n    \n    setupViews()\n    \n    nameLabel.text = viewModel.name\n    subtitleLabel.text = viewModel.digits\n    backgroundColor = viewModel.color\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(nameLabel)\n    addSubview(subtitleLabel)\n    backgroundColor = .systemIndigo\n    \n    NSLayoutConstraint.activate([\n      nameLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 24),\n      nameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      \n      subtitleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -24),\n      subtitleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/FinanceHomeBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\nimport AddPaymentMethod\nimport CombineUtil\nimport Topup\n\npublic protocol FinanceHomeDependency: Dependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n  var superPayRepository: SuperPayRepository { get }\n  var topupBuildable: TopupBuildable { get }\n  var addPaymentMethodBuildable: AddPaymentMethodBuildable { get }\n}\n\nfinal class FinanceHomeComponent: Component<FinanceHomeDependency>, SuperPayDashboardDependency, CardOnFileDashboardDependency {\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n  var superPayRepository: SuperPayRepository { dependency.superPayRepository }\n  var balance: ReadOnlyCurrentValuePublisher<Double> { superPayRepository.balance }\n  var topupBuildable: TopupBuildable { dependency.topupBuildable }\n  var addPaymentMethodBuildable: AddPaymentMethodBuildable { dependency.addPaymentMethodBuildable }\n}\n\n// MARK: - Builder\n\npublic protocol FinanceHomeBuildable: Buildable {\n  func build(withListener listener: FinanceHomeListener) -> ViewableRouting\n}\n\npublic final class FinanceHomeBuilder: Builder<FinanceHomeDependency>, FinanceHomeBuildable {\n  \n  public override init(dependency: FinanceHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: FinanceHomeListener) -> ViewableRouting {\n    let viewController = FinanceHomeViewController()\n    \n    let component = FinanceHomeComponent(\n      dependency: dependency\n    )\n    \n    let interactor = FinanceHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    \n    let superPayDashboardBuilder = SuperPayDashboardBuilder(dependency: component)\n    let cardOnFileDashboardBuilder = CardOnFileDashboardBuilder(dependency: component)\n    \n    return FinanceHomeRouter(\n      interactor: interactor,\n      viewController: viewController,\n      superPayDashboardBuildable: superPayDashboardBuilder,\n      cardOnFileDashboardBuildable: cardOnFileDashboardBuilder,\n      addPaymentMethodBuildable: component.addPaymentMethodBuildable,\n      topupBuildable: component.topupBuildable\n    )\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/FinanceHomeInteractor.swift",
    "content": "import ModernRIBs\nimport UIKit\nimport SuperUI\nimport FinanceEntity\n\nprotocol FinanceHomeRouting: ViewableRouting {\n  func attachSuperPayDashboard()\n  func attachCardOnFileDashboard()\n  func attachAddPaymentMethod()\n  func detachAddPaymentMethod()\n  func attachTopup()\n  func detachTopup()\n}\n\nprotocol FinanceHomePresentable: Presentable {\n  var listener: FinanceHomePresentableListener? { get set }\n}\n\npublic protocol FinanceHomeListener: AnyObject {\n}\n\nfinal class FinanceHomeInteractor: PresentableInteractor<FinanceHomePresentable>, FinanceHomeInteractable, FinanceHomePresentableListener, AdaptivePresentationControllerDelegate {\n  \n  weak var router: FinanceHomeRouting?\n  weak var listener: FinanceHomeListener?\n  \n  let presentationDelegateProxy: AdaptivePresentationControllerDelegateProxy\n  \n  override init(presenter: FinanceHomePresentable) {\n    self.presentationDelegateProxy = AdaptivePresentationControllerDelegateProxy()\n    super.init(presenter: presenter)\n    presenter.listener = self\n    self.presentationDelegateProxy.delegate = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    router?.attachSuperPayDashboard()\n    router?.attachCardOnFileDashboard()\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n  \n  func presentationControllerDidDismiss() {\n    router?.detachAddPaymentMethod()\n  }\n  \n  // MARK: - CardOnFileDashboardListener\n  func cardOnFileDashboardDidTapAddPaymentMethod() {\n    router?.attachAddPaymentMethod()\n  }\n  \n  // MARK: - AddPaymentMethodListener\n  func addPaymentMethodDidTapClose() {\n    router?.detachAddPaymentMethod()\n  }\n  \n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    router?.detachAddPaymentMethod()\n  }\n  \n  func superPayDashboardDidTapTopup() {\n    router?.attachTopup()\n  }\n  \n  func topupDidClose() {\n    router?.detachTopup()\n  }\n  \n  func topupDidFinish() {\n    router?.detachTopup()\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/FinanceHomeRouter.swift",
    "content": "import ModernRIBs\nimport SuperUI\nimport AddPaymentMethod\nimport Topup\nimport RIBsUtil\n\nprotocol FinanceHomeInteractable: Interactable, SuperPayDashboardListener, CardOnFileDashboardListener, AddPaymentMethodListener, TopupListener {\n  var router: FinanceHomeRouting? { get set }\n  var listener: FinanceHomeListener? { get set }\n  var presentationDelegateProxy: AdaptivePresentationControllerDelegateProxy { get }\n}\n\nprotocol FinanceHomeViewControllable: ViewControllable {\n  func addDashboard(_ view: ViewControllable)\n}\n\nfinal class FinanceHomeRouter: ViewableRouter<FinanceHomeInteractable, FinanceHomeViewControllable>, FinanceHomeRouting {\n  \n  private let superPayDashboardBuildable: SuperPayDashboardBuildable\n  private var superPayRouting: Routing?\n  \n  private let cardOnFileDashboardBuildable: CardOnFileDashboardBuildable\n  private var cardOnFileRouting: Routing?\n  \n  private let addPaymentMethodBuildable: AddPaymentMethodBuildable\n  private var addPaymentMethodRouting: Routing?\n  \n  private let topupBuildable: TopupBuildable\n  private var topupRouting: Routing?\n  \n  init(\n    interactor: FinanceHomeInteractable,\n    viewController: FinanceHomeViewControllable,\n    superPayDashboardBuildable: SuperPayDashboardBuildable,\n    cardOnFileDashboardBuildable: CardOnFileDashboardBuildable,\n    addPaymentMethodBuildable: AddPaymentMethodBuildable,\n    topupBuildable: TopupBuildable\n  ) {\n    self.superPayDashboardBuildable = superPayDashboardBuildable\n    self.cardOnFileDashboardBuildable = cardOnFileDashboardBuildable\n    self.addPaymentMethodBuildable = addPaymentMethodBuildable\n    self.topupBuildable = topupBuildable\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachSuperPayDashboard() {\n    if superPayRouting != nil {\n      return\n    }\n    \n    let router = superPayDashboardBuildable.build(withListener: interactor)\n    \n    let dashboard = router.viewControllable\n    viewController.addDashboard(dashboard)\n    \n    self.superPayRouting = router\n    attachChild(router)\n  }\n  \n  func attachCardOnFileDashboard() {\n    if cardOnFileRouting != nil {\n      return\n    }\n    \n    let router = cardOnFileDashboardBuildable.build(withListener: interactor)\n    let dashboard = router.viewControllable\n    viewController.addDashboard(dashboard)\n    \n    self.cardOnFileRouting = router\n    attachChild(router)\n  }\n  \n  func attachAddPaymentMethod() {\n    if addPaymentMethodRouting != nil {\n      return\n    }\n    \n    let router = addPaymentMethodBuildable.build(withListener: interactor, closeButtonType: .close)\n    let navigation = NavigationControllerable(root: router.viewControllable)\n    navigation.navigationController.presentationController?.delegate = interactor.presentationDelegateProxy\n    viewControllable.present(navigation, animated: true, completion: nil)\n    \n    addPaymentMethodRouting = router\n    attachChild(router)\n  }\n  \n  func detachAddPaymentMethod() {\n    guard let router = addPaymentMethodRouting else {\n      return\n    }\n    \n    viewControllable.dismiss(completion: nil)\n    \n    detachChild(router)\n    addPaymentMethodRouting = nil\n  }\n\n  func attachTopup() {\n    if topupRouting != nil {\n      return\n    }\n    \n    let router = topupBuildable.build(withListener: interactor)\n    topupRouting = router\n    attachChild(router)\n  }\n  \n  func detachTopup() {\n    guard let router = topupRouting else {\n      return\n    }\n    \n    detachChild(router)\n    self.topupRouting = nil\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/FinanceHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol FinanceHomePresentableListener: AnyObject {\n}\n\nfinal class FinanceHomeViewController: UIViewController, FinanceHomePresentable, FinanceHomeViewControllable {\n  \n  weak var listener: FinanceHomePresentableListener?\n  \n  private let stackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.axis = .vertical\n    stackView.alignment = .fill\n    stackView.distribution = .equalSpacing\n    stackView.spacing = 4\n    return stackView\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  func setupViews() {\n    title = \"슈퍼페이\"\n    tabBarItem = UITabBarItem(title: \"슈퍼페이\", image: UIImage(systemName: \"creditcard\"), selectedImage: UIImage(systemName: \"creditcard.fill\"))\n    tabBarItem.accessibilityIdentifier = \"superpay_home_tab_bar_item\"\n    view.backgroundColor = .white\n    view.addSubview(stackView)\n    \n    NSLayoutConstraint.activate([\n      stackView.topAnchor.constraint(equalTo: view.topAnchor),\n      stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor)\n    ])\n  }\n  \n  func addDashboard(_ view: ViewControllable) {\n    let vc = view.uiviewController\n    \n    addChild(vc)\n    stackView.addArrangedSubview(vc.view)\n    vc.didMove(toParent: self)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/SuperPayDashboard/Formatter.swift",
    "content": "import Foundation\n\nstruct Formatter {\n  static let balanceFormatter: NumberFormatter = {\n    let formatter = NumberFormatter()\n    formatter.numberStyle = .decimal\n    return formatter\n  }()\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/SuperPayDashboard/SuperPayDashboardBuilder.swift",
    "content": "import ModernRIBs\nimport Foundation\nimport CombineUtil\n\nprotocol SuperPayDashboardDependency: Dependency {\n  var balance: ReadOnlyCurrentValuePublisher<Double> { get }\n}\n\nfinal class SuperPayDashboardComponent: Component<SuperPayDashboardDependency>, SuperPayDashboardInteractorDependency {\n  var balanceFormatter: NumberFormatter { Formatter.balanceFormatter }\n  var balance: ReadOnlyCurrentValuePublisher<Double> { dependency.balance }\n}\n\n// MARK: - Builder\n\nprotocol SuperPayDashboardBuildable: Buildable {\n  func build(withListener listener: SuperPayDashboardListener) -> SuperPayDashboardRouting\n}\n\nfinal class SuperPayDashboardBuilder: Builder<SuperPayDashboardDependency>, SuperPayDashboardBuildable {\n  \n  override init(dependency: SuperPayDashboardDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: SuperPayDashboardListener) -> SuperPayDashboardRouting {\n    let component = SuperPayDashboardComponent(dependency: dependency)\n    let viewController = SuperPayDashboardViewController()\n    let interactor = SuperPayDashboardInteractor(\n      presenter: viewController,\n      dependency: component\n    )\n    interactor.listener = listener\n    return SuperPayDashboardRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/SuperPayDashboard/SuperPayDashboardInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport Foundation\nimport CombineUtil\n\nprotocol SuperPayDashboardRouting: ViewableRouting {\n}\n\nprotocol SuperPayDashboardPresentable: Presentable {\n  var listener: SuperPayDashboardPresentableListener? { get set }\n  \n  func updateBalance(_ balance: String)\n}\n\nprotocol SuperPayDashboardListener: AnyObject {\n  func superPayDashboardDidTapTopup()\n}\n\nprotocol SuperPayDashboardInteractorDependency {\n  var balance: ReadOnlyCurrentValuePublisher<Double> { get }\n  var balanceFormatter: NumberFormatter { get }\n}\n\nfinal class SuperPayDashboardInteractor: PresentableInteractor<SuperPayDashboardPresentable>, SuperPayDashboardInteractable, SuperPayDashboardPresentableListener {\n  \n  weak var router: SuperPayDashboardRouting?\n  weak var listener: SuperPayDashboardListener?\n  \n  private let dependency: SuperPayDashboardInteractorDependency\n  \n  private var cancellables: Set<AnyCancellable>\n  \n  init(\n    presenter: SuperPayDashboardPresentable,\n    dependency: SuperPayDashboardInteractorDependency\n  ) {\n    self.dependency = dependency\n    self.cancellables = .init()\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    dependency.balance\n      .receive(on: DispatchQueue.main)\n      .sink { [weak self] balance in\n      self?.dependency.balanceFormatter.string(from: NSNumber(value: balance)).map({\n        self?.presenter.updateBalance($0)\n      })\n    }.store(in: &cancellables)\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n  \n  func topupButtonDidTap() {\n    listener?.superPayDashboardDidTapTopup()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/SuperPayDashboard/SuperPayDashboardRouter.swift",
    "content": "import ModernRIBs\n\nprotocol SuperPayDashboardInteractable: Interactable {\n  var router: SuperPayDashboardRouting? { get set }\n  var listener: SuperPayDashboardListener? { get set }\n}\n\nprotocol SuperPayDashboardViewControllable: ViewControllable {\n}\n\nfinal class SuperPayDashboardRouter: ViewableRouter<SuperPayDashboardInteractable, SuperPayDashboardViewControllable>, SuperPayDashboardRouting {\n  \n  override init(interactor: SuperPayDashboardInteractable, viewController: SuperPayDashboardViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceHome/SuperPayDashboard/SuperPayDashboardViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol SuperPayDashboardPresentableListener: AnyObject {\n  func topupButtonDidTap()\n}\n\nfinal class SuperPayDashboardViewController: UIViewController, SuperPayDashboardPresentable, SuperPayDashboardViewControllable {\n  \n  weak var listener: SuperPayDashboardPresentableListener?\n  \n  private let headerStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    stackView.alignment = .fill\n    stackView.distribution = .equalSpacing\n    stackView.axis = .horizontal\n    return stackView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 22, weight: .semibold)\n    label.text = \"슈퍼페이 잔고\"\n    return label\n  }()\n  \n  private lazy var topupButton: UIButton = {\n    let button = UIButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.setTitle(\"충전하기\", for: .normal)\n    button.setTitleColor(.systemBlue, for: .normal)\n    button.accessibilityIdentifier = \"superpay_dashboard_topup_button\"\n    button.addTarget(self, action: #selector(topupButtonDidTap), for: .touchUpInside)\n    return button\n  }()\n  \n  private let cardView: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.layer.cornerRadius = 16\n    view.layer.cornerCurve = .continuous\n    view.backgroundColor = .systemIndigo\n    return view\n  }()\n  \n  private let currencyLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 22, weight: .semibold)\n    label.text = \"원\"\n    label.textColor = .white\n    return label\n  }()\n  \n  private let balanceAmountLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = .systemFont(ofSize: 22, weight: .semibold)\n    label.accessibilityIdentifier = \"superpay_dashboard_balance_label\"\n    label.textColor = .white\n    return label\n  }()\n  \n  private let balanceStackView: UIStackView = {\n    let stack = UIStackView()\n    stack.translatesAutoresizingMaskIntoConstraints = false\n    stack.alignment = .fill\n    stack.distribution = .equalSpacing\n    stack.axis = .horizontal\n    stack.spacing = 4\n    return stack\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    view.addSubview(headerStackView)\n    view.addSubview(cardView)\n    \n    headerStackView.addArrangedSubview(titleLabel)\n    headerStackView.addArrangedSubview(topupButton)\n    \n    cardView.addSubview(balanceStackView)\n    balanceStackView.addArrangedSubview(balanceAmountLabel)\n    balanceStackView.addArrangedSubview(currencyLabel)\n    \n    NSLayoutConstraint.activate([\n      headerStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),\n      headerStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      headerStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      cardView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 10),\n      cardView.heightAnchor.constraint(equalToConstant: 180),\n      cardView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      cardView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      cardView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20),\n      \n      balanceStackView.centerXAnchor.constraint(equalTo: cardView.centerXAnchor),\n      balanceStackView.centerYAnchor.constraint(equalTo: cardView.centerYAnchor)\n    ])\n  }\n  \n  func updateBalance(_ balance: String) {\n    balanceAmountLabel.text = balance\n  }\n  \n  @objc\n  private func topupButtonDidTap() {\n    listener?.topupButtonDidTap()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepository/AddCardRequest.swift",
    "content": "import Foundation\nimport Network\nimport FinanceEntity\n\nstruct AddCardRequest: Request {\n  typealias Output = AddCardResponse\n  \n  let endpoint: URL\n  let method: HTTPMethod\n  let query: QueryItems\n  let header: HTTPHeader\n  \n  init(baseURL: URL, info: AddPaymentMethodInfo) {\n    self.endpoint = baseURL.appendingPathComponent(\"/addCard\")\n    self.method = .post\n    self.query = [\n      \"number\": info.number,\n      \"cvc\": info.cvc,\n      \"expiry\": info.expiration\n    ]\n    self.header = [:]\n  }\n}\n\nstruct AddCardResponse: Decodable {\n  let card: PaymentMethod\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepository/CardOnFileRepository.swift",
    "content": "import Foundation\nimport Combine\nimport FinanceEntity\nimport CombineUtil\nimport Network\n\npublic protocol CardOnFileRepository {\n  var cardOnFile: ReadOnlyCurrentValuePublisher<[PaymentMethod]> { get }\n  func addCard(info: AddPaymentMethodInfo) -> AnyPublisher<PaymentMethod, Error>\n  func fetch()\n}\n\npublic final class CardOnFileRepositoryImp: CardOnFileRepository {\n  public var cardOnFile: ReadOnlyCurrentValuePublisher<[PaymentMethod]> { paymentMethodsSubject }\n  \n  private let paymentMethodsSubject = CurrentValuePublisher<[PaymentMethod]>([\n//    PaymentMethod(id: \"0\", name: \"우리은행\", digits: \"0123\", color: \"#f19a38ff\", isPrimary: false),\n//    PaymentMethod(id: \"1\", name: \"신한카드\", digits: \"0987\", color: \"#3478f6ff\", isPrimary: false),\n//    PaymentMethod(id: \"2\", name: \"현대카드\", digits: \"8121\", color: \"#78c5f5ff\", isPrimary: false),\n//    PaymentMethod(id: \"3\", name: \"국민은행\", digits: \"2812\", color: \"#65c466ff\", isPrimary: false),\n//    PaymentMethod(id: \"4\", name: \"카카오뱅크\", digits: \"8751\", color: \"#ffcc00ff\", isPrimary: false)\n  ])\n  \n  public func addCard(info: AddPaymentMethodInfo) -> AnyPublisher<PaymentMethod, Error> {\n    let request = AddCardRequest(baseURL: baseURL, info: info)\n    return network.send(request)\n      .map(\\.output.card)\n      .handleEvents(\n        receiveSubscription: nil,\n        receiveOutput: { [weak self] method in\n          guard let this = self else {\n            return\n          }\n          this.paymentMethodsSubject.send(this.paymentMethodsSubject.value + [method])\n        },\n        receiveCompletion: nil,\n        receiveCancel: nil,\n        receiveRequest: nil\n      )\n      .eraseToAnyPublisher()\n  }\n  \n  public func fetch() {\n    let request = CardOnFileRequest(baseURL: baseURL)\n    network.send(request).map(\\.output.cards)\n      .sink(\n        receiveCompletion: { _ in },\n        receiveValue: { [weak self] cards in\n          self?.paymentMethodsSubject.send(cards)\n        }\n      ).store(in: &cancellables)\n  }\n  \n  private let network: Network\n  private let baseURL: URL\n  private var cancellables: Set<AnyCancellable>\n  \n  public init(network: Network, baseURL: URL) {\n    self.network = network\n    self.baseURL = baseURL\n    self.cancellables = .init()\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepository/CardOnFileRequest.swift",
    "content": "import Foundation\nimport Network\nimport FinanceEntity\n\nstruct CardOnFileRequest: Request {\n  typealias Output = CardOnFileResponse\n  \n  let endpoint: URL\n  let method: HTTPMethod\n  let query: QueryItems\n  let header: HTTPHeader\n  \n  init(baseURL: URL) {\n    self.endpoint = baseURL.appendingPathComponent(\"/cards\")\n    self.method = .get\n    self.query = [:]\n    self.header = [:]\n  }\n}\n\nstruct CardOnFileResponse: Decodable {\n  let cards: [PaymentMethod]\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepository/SuperPayRepository.swift",
    "content": "import Foundation\nimport Combine\nimport CombineUtil\nimport Network\n\npublic protocol SuperPayRepository {\n  var balance: ReadOnlyCurrentValuePublisher<Double> { get }\n  func topup(amount: Double, paymentMethodID: String) -> AnyPublisher<Void, Error>\n}\n\npublic final class SuperPayRepositoryImp: SuperPayRepository {\n  \n  public var balance: ReadOnlyCurrentValuePublisher<Double> { balanceSubject }\n  private let balanceSubject = CurrentValuePublisher<Double>(0)\n  \n  public func topup(amount: Double, paymentMethodID: String) -> AnyPublisher<Void, Error> {\n    let request = TopupRequest(baseURL: baseURL, amount: amount, paymentMethodID: paymentMethodID)\n    \n    return network.send(request)\n      .handleEvents(\n        receiveSubscription: nil,\n        receiveOutput: { [weak self] _ in\n          let newBalance = (self?.balanceSubject.value).map { $0 + amount }\n          newBalance.map { self?.balanceSubject.send($0) }\n        },\n        receiveCompletion: nil,\n        receiveCancel: nil,\n        receiveRequest: nil\n      )\n      .map({ _ in })\n      .eraseToAnyPublisher()\n  }\n  \n  private let bgQueue = DispatchQueue(label: \"topup.repository.queue\")\n  \n  private let network: Network\n  private let baseURL: URL\n  \n  public init(network: Network, baseURL: URL) {\n    self.network = network\n    self.baseURL = baseURL\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepository/TopupRequest.swift",
    "content": "import Foundation\nimport Network\n\nstruct TopupRequest: Request {\n  typealias Output = TopupResponse\n  \n  let endpoint: URL\n  let method: HTTPMethod\n  let query: QueryItems\n  let header: HTTPHeader\n    \n  init(baseURL: URL, amount: Double, paymentMethodID: String) {\n    self.endpoint = baseURL.appendingPathComponent(\"/topup\")\n    self.method = .post\n    self.query = [\n      \"amount\": amount,\n      \"paymentMethodID\": paymentMethodID\n    ]\n    self.header = [:]\n  }\n  \n}\n\nstruct TopupResponse: Decodable {\n  let status: String\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepositoryTestSupport/CardOnFileRepositoryMock.swift",
    "content": "import Foundation\nimport FinanceRepository\nimport CombineUtil\nimport Combine\nimport FinanceEntity\n\npublic final class CardOnFileRepositoryMock: CardOnFileRepository {\n  \n  public var cardOnFileSubject: CurrentValuePublisher<[PaymentMethod]> = .init([])\n  public var cardOnFile: ReadOnlyCurrentValuePublisher<[PaymentMethod]> { cardOnFileSubject }\n  \n  public var addCardCallCount = 0\n  public var addCardInfo: AddPaymentMethodInfo?\n  public var addedPaymentMethod: PaymentMethod?\n  public func addCard(info: AddPaymentMethodInfo) -> AnyPublisher<PaymentMethod, Error> {\n    addCardCallCount += 1\n    addCardInfo = info\n    \n    if let addedPaymentMethod = addedPaymentMethod {\n      return Just(addedPaymentMethod).setFailureType(to: Error.self).eraseToAnyPublisher()\n    } else {\n      return Fail(error: NSError(domain: \"CardOnFileRepositoryMock\", code: 0, userInfo: nil)).eraseToAnyPublisher()\n    }\n  }\n  \n  public var fetchCallCount = 0\n  public func fetch() {\n    fetchCallCount += 1\n  }\n  \n  public init() {\n    \n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/FinanceRepositoryTestSupport/SuperPayRepositoryMock.swift",
    "content": "import Foundation\nimport FinanceRepository\nimport CombineUtil\nimport Combine\n\npublic final class SuperPayRepositoryMock: SuperPayRepository {\n  \n  public var balanceSubject = CurrentValuePublisher<Double>(0)\n  public var balance: ReadOnlyCurrentValuePublisher<Double> { balanceSubject }\n  \n  public var topupCallCount = 0\n  public var topupAmount: Double?\n  public var paymentMethodID: String?\n  public var shouldTopupSucceed: Bool = true\n  public func topup(amount: Double, paymentMethodID: String) -> AnyPublisher<Void, Error> {\n    topupCallCount += 1\n    topupAmount = amount\n    self.paymentMethodID = paymentMethodID\n    \n    if shouldTopupSucceed {\n      return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher()\n    } else {\n      return Fail(error: NSError(domain: \"SuperPayRepositoryMock\", code: 0, userInfo: nil)).eraseToAnyPublisher()\n    }\n  }\n  \n  public init() {\n    \n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/Topup/TopupInterface.swift",
    "content": "import Foundation\nimport ModernRIBs\n\npublic protocol TopupBuildable: Buildable {\n  func build(withListener listener: TopupListener) -> Routing\n}\n\npublic protocol TopupListener: AnyObject {\n  func topupDidClose()\n  func topupDidFinish()\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/Array+Utils.swift",
    "content": "import Foundation\n\nextension Array {\n  subscript(safe index: Int) -> Element? {\n    return indices ~= index ? self[index] : nil\n  }\n}\n\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/CardOnFile/CardOnFileBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceEntity\n\nprotocol CardOnFileDependency: Dependency {\n}\n\nfinal class CardOnFileComponent: Component<CardOnFileDependency> {\n}\n\n// MARK: - Builder\n\nprotocol CardOnFileBuildable: Buildable {\n  func build(withListener listener: CardOnFileListener, paymentMethods: [PaymentMethod]) -> CardOnFileRouting\n}\n\nfinal class CardOnFileBuilder: Builder<CardOnFileDependency>, CardOnFileBuildable {\n  \n  override init(dependency: CardOnFileDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: CardOnFileListener, paymentMethods: [PaymentMethod]) -> CardOnFileRouting {\n    _ = CardOnFileComponent(dependency: dependency)\n    let viewController = CardOnFileViewController()\n    let interactor = CardOnFileInteractor(presenter: viewController, paymentMethods: paymentMethods)\n    interactor.listener = listener\n    return CardOnFileRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/CardOnFile/CardOnFileCell.swift",
    "content": "import UIKit\n\nfinal class CardOnFileCell: UITableViewCell {\n  \n  func setImage(_ image: UIImage?) {\n    thumbnailView.image = image\n  }\n  \n  func setTitle(_ title: String) {\n    titleLabel.text = title\n  }\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFill\n    imageView.clipsToBounds = true\n    imageView.roundCorners(4)\n    return imageView\n  }()\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {\n    super.init(style: style, reuseIdentifier: reuseIdentifier)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    contentView.addSubview(thumbnailView)\n    contentView.addSubview(titleLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      thumbnailView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),\n      \n      titleLabel.centerYAnchor.constraint(equalTo: thumbnailView.centerYAnchor),\n      titleLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 14)\n    ])\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/CardOnFile/CardOnFileInteractor.swift",
    "content": "import ModernRIBs\nimport FinanceEntity\n\nprotocol CardOnFileRouting: ViewableRouting {\n}\n\nprotocol CardOnFilePresentable: Presentable {\n  var listener: CardOnFilePresentableListener? { get set }\n  \n  func update(with viewModels: [PaymentMethodViewModel])\n}\n\nprotocol CardOnFileListener: AnyObject {\n  func cardOnFileDidTapClose()\n  func cardOnFileDidTapAddCard()\n  func cardOnFileDidSelect(at index: Int)\n}\n\nfinal class CardOnFileInteractor: PresentableInteractor<CardOnFilePresentable>, CardOnFileInteractable, CardOnFilePresentableListener {\n  \n  weak var router: CardOnFileRouting?\n  weak var listener: CardOnFileListener?\n  \n  private let paymentMethods: [PaymentMethod]\n  \n  init(\n    presenter: CardOnFilePresentable,\n    paymentMethods: [PaymentMethod]\n  ) {\n    self.paymentMethods = paymentMethods\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    presenter.update(with: paymentMethods.map(PaymentMethodViewModel.init))\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n  \n  func didTapClose() {\n    listener?.cardOnFileDidTapClose()\n  }\n  \n  func didSelectItem(at index: Int) {\n    if index >= paymentMethods.count {\n      listener?.cardOnFileDidTapAddCard()\n    } else {\n      listener?.cardOnFileDidSelect(at: index)\n    }\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/CardOnFile/CardOnFileRouter.swift",
    "content": "import ModernRIBs\n\nprotocol CardOnFileInteractable: Interactable {\n  var router: CardOnFileRouting? { get set }\n  var listener: CardOnFileListener? { get set }\n}\n\nprotocol CardOnFileViewControllable: ViewControllable {\n}\n\nfinal class CardOnFileRouter: ViewableRouter<CardOnFileInteractable, CardOnFileViewControllable>, CardOnFileRouting {\n  \n  override init(interactor: CardOnFileInteractable, viewController: CardOnFileViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/CardOnFile/CardOnFileViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol CardOnFilePresentableListener: AnyObject {\n  func didTapClose()\n  func didSelectItem(at: Int)\n}\n\nfinal class CardOnFileViewController: UIViewController, CardOnFilePresentable, CardOnFileViewControllable, UITableViewDataSource, UITableViewDelegate {\n  \n  weak var listener: CardOnFilePresentableListener?\n  \n  func update(with viewModels: [PaymentMethodViewModel]) {\n    self.viewModels = viewModels\n    tableView.reloadData()\n  }\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private var viewModels: [PaymentMethodViewModel] = []\n  \n  private lazy var tableView: UITableView = {\n    let tableView = UITableView()\n    tableView.translatesAutoresizingMaskIntoConstraints = false\n    tableView.dataSource = self\n    tableView.delegate = self\n    tableView.register(cellType: CardOnFileCell.self)\n    tableView.tableFooterView = UIView()\n    tableView.rowHeight = 60\n    tableView.separatorInset = .zero\n    return tableView\n  }()\n  \n  private func setupViews() {\n    title = \"카드 선택\"\n    view.backgroundColor = .white\n    view.addSubview(tableView)\n    \n    setupNavigationItem(with: .back, target: self, action: #selector(didTapClose))\n    \n    NSLayoutConstraint.activate([\n      tableView.topAnchor.constraint(equalTo: view.topAnchor),\n      tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n    ])\n  }\n  \n  // MARK: - UITableView\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return viewModels.count + 1\n  }\n  \n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    let cell: CardOnFileCell = tableView.dequeueReusableCell(for: indexPath)\n    \n    if let viewModel = viewModels[safe: indexPath.row] {\n      cell.setImage(UIImage(color: viewModel.color))\n      cell.setTitle(\"\\(viewModel.name) \\(viewModel.digits)\")\n    } else {\n      cell.setImage(UIImage(systemName: \"plus.rectangle\"))\n      cell.setTitle(\"카드 추가\")\n    }\n    \n    return cell\n  }\n  \n  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n    tableView.deselectRow(at: indexPath, animated: true)\n    \n    listener?.didSelectItem(at: indexPath.row)\n  }\n  \n  @objc\n  private func didTapClose() {\n    listener?.didTapClose()\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/EnterAmountBuilder.swift",
    "content": "import ModernRIBs\nimport CombineUtil\nimport FinanceEntity\nimport FinanceRepository\nimport CombineSchedulers\n\nprotocol EnterAmountDependency: Dependency {\n  var selectedPaymentMethod: ReadOnlyCurrentValuePublisher<PaymentMethod> { get }\n  var superPayRepository: SuperPayRepository { get }\n  var mainQueue: AnySchedulerOf<DispatchQueue> { get }\n}\n\nfinal class EnterAmountComponent: Component<EnterAmountDependency>, EnterAmountInteractorDependency {\n  var selectedPaymentMethod: ReadOnlyCurrentValuePublisher<PaymentMethod> { dependency.selectedPaymentMethod }\n  var superPayRepository: SuperPayRepository { dependency.superPayRepository }\n  var mainQueue: AnySchedulerOf<DispatchQueue> { dependency.mainQueue }\n}\n\n// MARK: - Builder\n\nprotocol EnterAmountBuildable: Buildable {\n  func build(withListener listener: EnterAmountListener) -> EnterAmountRouting\n}\n\nfinal class EnterAmountBuilder: Builder<EnterAmountDependency>, EnterAmountBuildable {\n  \n  override init(dependency: EnterAmountDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build(withListener listener: EnterAmountListener) -> EnterAmountRouting {\n    let component = EnterAmountComponent(dependency: dependency)\n    let viewController = EnterAmountViewController()\n    let interactor = EnterAmountInteractor(presenter: viewController, dependency: component)\n    interactor.listener = listener\n    return EnterAmountRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/EnterAmountInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport Foundation\nimport CombineUtil\nimport FinanceEntity\nimport FinanceRepository\nimport CombineSchedulers\n\nprotocol EnterAmountRouting: ViewableRouting {\n}\n\nprotocol EnterAmountPresentable: Presentable {\n  var listener: EnterAmountPresentableListener? { get set }\n  \n  func updateSelectedPaymentMethod(with viewModel: SelectedPaymentMethodViewModel)\n  func startLoading()\n  func stopLoading()\n}\n\nprotocol EnterAmountListener: AnyObject {\n  func enterAmountDidTapClose()\n  func enterAmountDidTapPaymentMethod()\n  func enterAmountDidFinishTopup()\n}\n\nprotocol EnterAmountInteractorDependency {\n  var selectedPaymentMethod: ReadOnlyCurrentValuePublisher<PaymentMethod> { get }\n  var superPayRepository: SuperPayRepository { get }\n  var mainQueue: AnySchedulerOf<DispatchQueue> { get }\n}\n\nfinal class EnterAmountInteractor: PresentableInteractor<EnterAmountPresentable>, EnterAmountInteractable, EnterAmountPresentableListener {\n  \n  weak var router: EnterAmountRouting?\n  weak var listener: EnterAmountListener?\n  \n  private let dependency: EnterAmountInteractorDependency\n  \n  private var cancellables: Set<AnyCancellable>\n  \n  init(\n    presenter: EnterAmountPresentable,\n    dependency: EnterAmountInteractorDependency\n  ) {\n    self.dependency = dependency\n    self.cancellables = .init()\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    dependency.selectedPaymentMethod.sink { [weak self] paymentMethod in\n      self?.presenter.updateSelectedPaymentMethod(with: SelectedPaymentMethodViewModel(paymentMethod))\n    }.store(in: &cancellables)\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n  \n  func didTapClose() {\n    listener?.enterAmountDidTapClose()\n  }\n  \n  func didTapPaymentMethod() {\n    listener?.enterAmountDidTapPaymentMethod()\n  }\n  \n  func didTapTopup(with amount: Double) {\n    presenter.startLoading()\n    \n    dependency.superPayRepository.topup(\n      amount: amount,\n      paymentMethodID: dependency.selectedPaymentMethod.value.id\n    )\n      .receive(on: dependency.mainQueue)\n      .sink(\n        receiveCompletion: { [weak self] _ in\n          self?.presenter.stopLoading()\n        },\n        receiveValue: { [weak self] in\n          self?.listener?.enterAmountDidFinishTopup()\n        }\n      ).store(in: &cancellables)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/EnterAmountRouter.swift",
    "content": "import ModernRIBs\n\nprotocol EnterAmountInteractable: Interactable {\n  var router: EnterAmountRouting? { get set }\n  var listener: EnterAmountListener? { get set }\n}\n\nprotocol EnterAmountViewControllable: ViewControllable {\n}\n\nfinal class EnterAmountRouter: ViewableRouter<EnterAmountInteractable, EnterAmountViewControllable>, EnterAmountRouting {\n  \n  override init(interactor: EnterAmountInteractable, viewController: EnterAmountViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/EnterAmountViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol EnterAmountPresentableListener: AnyObject {\n  func didTapClose()\n  func didTapPaymentMethod()\n  func didTapTopup(with amount: Double)\n}\n\nfinal class EnterAmountViewController: UIViewController, EnterAmountPresentable, EnterAmountViewControllable {\n  \n  weak var listener: EnterAmountPresentableListener?\n  \n  func updateSelectedPaymentMethod(with viewModel: SelectedPaymentMethodViewModel) {\n    selectedPaymentMethodView.update(with: viewModel)\n  }\n  \n  func startLoading() {\n    activityIndicator.startAnimating()\n    ctaButton.isEnabled = false\n  }\n  \n  func stopLoading() {\n    activityIndicator.stopAnimating()\n    ctaButton.isEnabled = true\n  }\n  \n  private lazy var selectedPaymentMethodView: SelectedPaymentMethodView = {\n    let view = SelectedPaymentMethodView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.addShadowWithRoundedCorners()\n    let tap = UITapGestureRecognizer(target: self, action: #selector(didTapPaymentMethod))\n    view.addGestureRecognizer(tap)\n    return view\n  }()\n  \n  private let enterAmountWidget: EnterAmountWidget = {\n    let widget = EnterAmountWidget()\n    widget.translatesAutoresizingMaskIntoConstraints = false\n    widget.addShadowWithRoundedCorners()\n    return widget\n  }()\n  \n  private lazy var ctaButton: UIButton = {\n    let cta = UIButton(type: .system)\n    cta.translatesAutoresizingMaskIntoConstraints = false\n    cta.roundCorners()\n    cta.setTitle(\"충전\", for: .normal)\n    cta.titleLabel?.font = UIFont.systemFont(ofSize: 20, weight: .semibold)\n    cta.setBackgroundImage(UIImage(color: .primaryRed), for: .normal)\n    cta.tintColor = .white\n    cta.accessibilityIdentifier = \"topup_enteramount_confirm_button\"\n    cta.addTarget(self, action: #selector(didTapCTAButton), for: .touchUpInside)\n    return cta\n  }()\n  \n  private let activityIndicator: UIActivityIndicatorView = {\n    let activity = UIActivityIndicatorView(style: .medium)\n    activity.translatesAutoresizingMaskIntoConstraints = false\n    activity.hidesWhenStopped = true\n    activity.stopAnimating()\n    return activity\n  }()\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    title = \"충전하기\"\n    view.backgroundColor = .backgroundColor\n    \n    setupNavigationItem(with: .close, target: self, action: #selector(didTapClose))\n    \n    view.addSubview(selectedPaymentMethodView)\n    view.addSubview(enterAmountWidget)\n    view.addSubview(ctaButton)\n    view.addSubview(activityIndicator)\n    \n    NSLayoutConstraint.activate([\n      selectedPaymentMethodView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      selectedPaymentMethodView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),\n      selectedPaymentMethodView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      selectedPaymentMethodView.heightAnchor.constraint(equalToConstant: 70),\n      \n      enterAmountWidget.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      enterAmountWidget.topAnchor.constraint(equalTo: selectedPaymentMethodView.bottomAnchor, constant: 20),\n      enterAmountWidget.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      ctaButton.heightAnchor.constraint(equalToConstant: 60),\n      ctaButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n      ctaButton.topAnchor.constraint(equalTo: enterAmountWidget.bottomAnchor, constant: 40),\n      ctaButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      \n      activityIndicator.centerXAnchor.constraint(equalTo: ctaButton.centerXAnchor),\n      activityIndicator.centerYAnchor.constraint(equalTo: ctaButton.centerYAnchor),\n    ])\n  }\n  \n  @objc\n  private func didTapClose() {\n    listener?.didTapClose()\n  }\n  \n  @objc\n  private func didTapCTAButton() {\n    if let amount = enterAmountWidget.text.flatMap(Double.init) {\n      listener?.didTapTopup(with: amount)\n    }\n  }\n  \n  @objc\n  private func didTapPaymentMethod() {\n    listener?.didTapPaymentMethod()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/EnterAmountWidget.swift",
    "content": "import UIKit\n\nfinal class EnterAmountWidget: UIView {\n  \n  var text: String? {\n    amountTextField.text\n  }\n\n  init() {\n    super.init(frame: .zero)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let titleLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    label.text = \"금액\"\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  private lazy var amountStackView: UIStackView = {\n    let stackView = UIStackView()\n    stackView.translatesAutoresizingMaskIntoConstraints = false\n    let button = UIButton()\n    stackView.axis = .horizontal\n    stackView.alignment = .fill\n    stackView.distribution = .fill\n    stackView.spacing = 5\n    stackView.addArrangedSubview(self.amountTextField)\n    stackView.addArrangedSubview(self.currencyLabel)\n    return stackView\n  }()\n  \n  private let amountTextField: UITextField = {\n    let textField = UITextField()\n    textField.translatesAutoresizingMaskIntoConstraints = false\n    textField.borderStyle = .none\n    textField.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    textField.textAlignment = .right\n    textField.keyboardType = .numberPad\n    textField.accessibilityIdentifier = \"topup_enteramount_textfield\"\n    return textField\n  }()\n  \n  private let currencyLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    label.text = \"원\"\n    return label\n  }()\n  \n  private func setupViews() {\n    self.backgroundColor = .white\n    self.addSubview(titleLabel)\n    self.addSubview(amountStackView)\n    \n    NSLayoutConstraint.activate([\n      titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 16),\n      titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16),\n      titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),\n      amountStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16),\n      amountStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),\n      amountStackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16),\n      amountStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16)\n    ])\n  }\n}\n\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/EnterAmount/SelectedPaymentMethodView.swift",
    "content": "import UIKit\nimport FinanceEntity\n\nstruct SelectedPaymentMethodViewModel {\n  let image: UIImage?\n  let name: String\n  \n  init(_ paymentMethod: PaymentMethod) {\n    image = UIColor(hex: paymentMethod.color).flatMap { UIImage(color: $0) }\n    name = \"\\(paymentMethod.name) \\(paymentMethod.digits)\"\n  }\n}\n\nfinal class SelectedPaymentMethodView: UIView {\n  \n  func update(with viewModel: SelectedPaymentMethodViewModel) {\n    self.thumbnailView.image = viewModel.image\n    self.nameLabel.text = viewModel.name\n  }\n  \n  init() {\n    super.init(frame: .zero)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleToFill\n    imageView.roundCorners(4)\n    imageView.backgroundColor = .systemGray3\n    return imageView\n  }()\n  \n  private let nameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)\n    label.numberOfLines = 1\n    return label\n  }()\n  \n  private let rightChevronIcon: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.image = UIImage(\n      systemName: \"chevron.right\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)\n    )\n    imageView.tintColor = .systemGray3\n    return imageView\n  }()\n  \n  private func setupViews() {\n    self.backgroundColor = .white\n    self.addSubview(thumbnailView)\n    self.addSubview(nameLabel)\n    self.addSubview(rightChevronIcon)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      thumbnailView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      nameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      nameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 22),\n      rightChevronIcon.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      rightChevronIcon.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -24)\n    ])\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/Models/PaymentMethodViewModel.swift",
    "content": "import UIKit\nimport FinanceEntity\n\nstruct PaymentMethodViewModel {\n  let name: String\n  let digits: String\n  let color: UIColor\n  \n  init(_ paymentMethod: PaymentMethod) {\n    name = paymentMethod.name\n    digits = \"**** \\(paymentMethod.digits)\"\n    color = UIColor(hex: paymentMethod.color) ?? .systemGray2\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/TopupBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\nimport CombineUtil\nimport AddPaymentMethod\nimport FinanceEntity\nimport Topup\nimport CombineSchedulers\n\npublic protocol TopupDependency: Dependency {\n  var topupBaseViewController: ViewControllable { get }\n  var cardOnFileRepository: CardOnFileRepository { get }\n  var superPayRepository: SuperPayRepository { get }\n  var addPaymentMethodBuildable: AddPaymentMethodBuildable { get }\n  var mainQueue: AnySchedulerOf<DispatchQueue> { get }\n}\n\nfinal class TopupComponent: Component<TopupDependency>, TopupInteractorDependency, EnterAmountDependency, CardOnFileDependency {\n  var superPayRepository: SuperPayRepository { dependency.superPayRepository }\n  var selectedPaymentMethod: ReadOnlyCurrentValuePublisher<PaymentMethod> { paymentMethodStream }\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n  var mainQueue: AnySchedulerOf<DispatchQueue> { dependency.mainQueue }\n  fileprivate var topupBaseViewController: ViewControllable { dependency.topupBaseViewController }\n  \n  let paymentMethodStream: CurrentValuePublisher<PaymentMethod>\n  \n  var addPaymentMethodBuildable: AddPaymentMethodBuildable { dependency.addPaymentMethodBuildable }\n  \n  init(\n    dependency: TopupDependency,\n    paymentMethodStream: CurrentValuePublisher<PaymentMethod>\n  ) {\n    self.paymentMethodStream = paymentMethodStream\n    super.init(dependency: dependency)\n  }\n}\n\n// MARK: - Builder\n\npublic final class TopupBuilder: Builder<TopupDependency>, TopupBuildable {\n  \n  public override init(dependency: TopupDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: TopupListener) -> Routing {\n    let paymentMethodStream = CurrentValuePublisher(PaymentMethod(id: \"\", name: \"\", digits: \"\", color: \"\", isPrimary: false))\n    \n    let component = TopupComponent(dependency: dependency, paymentMethodStream: paymentMethodStream)\n    let interactor = TopupInteractor(dependency: component)\n    interactor.listener = listener\n    \n    let enterAmountBuilder = EnterAmountBuilder(dependency: component)\n    let cardOnFileBuilder = CardOnFileBuilder(dependency: component)\n    \n    return TopupRouter(\n      interactor: interactor,\n      viewController: component.topupBaseViewController,\n      addPaymentMethodBuildable: component.addPaymentMethodBuildable,\n      enterAmountBuildable: enterAmountBuilder,\n      cardOnFileBuildable: cardOnFileBuilder\n    )\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/TopupInteractor.swift",
    "content": "import ModernRIBs\nimport RIBsUtil\nimport FinanceEntity\nimport FinanceRepository\nimport CombineUtil\nimport AddPaymentMethod\nimport SuperUI\nimport Topup\n\nprotocol TopupRouting: Routing {\n  func cleanupViews()\n  \n  func attachAddPaymentMethod(closeButtonType: DismissButtonType)\n  func detachAddPaymentMethod()\n  func attachEnterAmount()\n  func detachEnterAmount()\n  func attachCardOnFile(paymentMethods: [PaymentMethod])\n  func detachCardOnFile()\n  func popToRoot()\n}\n\nprotocol TopupInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n  var paymentMethodStream: CurrentValuePublisher<PaymentMethod> { get }\n}\n\nfinal class TopupInteractor: Interactor, TopupInteractable, AddPaymentMethodListener, AdaptivePresentationControllerDelegate {\n  \n  weak var router: TopupRouting?\n  weak var listener: TopupListener?\n  \n  let presentationDelegateProxy: AdaptivePresentationControllerDelegateProxy\n  \n  private var isEnterAmountRoot: Bool = false\n  \n  private var paymentMethods: [PaymentMethod] {\n    dependency.cardOnFileRepository.cardOnFile.value\n  }\n  \n  private let dependency: TopupInteractorDependency\n  \n  init(\n    dependency: TopupInteractorDependency\n  ) {\n    self.presentationDelegateProxy = AdaptivePresentationControllerDelegateProxy()\n    self.dependency = dependency\n    super.init()\n    self.presentationDelegateProxy.delegate = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    if let card = dependency.cardOnFileRepository.cardOnFile.value.first {\n      isEnterAmountRoot = true\n      dependency.paymentMethodStream.send(card)\n      router?.attachEnterAmount()\n    } else {\n      isEnterAmountRoot = false\n      router?.attachAddPaymentMethod(closeButtonType: .close)\n    }\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    \n    router?.cleanupViews()\n  }\n  \n  func presentationControllerDidDismiss() {\n    listener?.topupDidClose()\n  }\n  \n  func addPaymentMethodDidTapClose() {\n    router?.detachAddPaymentMethod()\n    if isEnterAmountRoot == false {\n      listener?.topupDidClose()\n    }\n  }\n  \n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    dependency.paymentMethodStream.send(paymentMethod)\n    \n    if isEnterAmountRoot {\n      router?.popToRoot()\n    } else {\n      isEnterAmountRoot = true\n      router?.attachEnterAmount()\n    }\n  }\n  \n  func enterAmountDidTapClose() {\n    router?.detachEnterAmount()\n    listener?.topupDidClose()\n  }\n  \n  func enterAmountDidTapPaymentMethod() {\n    router?.attachCardOnFile(paymentMethods: paymentMethods)\n  }\n  \n  func enterAmountDidFinishTopup() {\n    listener?.topupDidFinish()\n  }\n  \n  func cardOnFileDidTapClose() {\n    router?.detachCardOnFile()\n  }\n  \n  func cardOnFileDidTapAddCard() {\n    router?.attachAddPaymentMethod(closeButtonType: .back)\n  }\n  \n  func cardOnFileDidSelect(at index: Int) {\n    if let selected = paymentMethods[safe: index] {\n      dependency.paymentMethodStream.send(selected)\n    }\n    router?.detachCardOnFile()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupImp/TopupRouter.swift",
    "content": "import ModernRIBs\nimport AddPaymentMethod\nimport SuperUI\nimport RIBsUtil\nimport FinanceEntity\nimport Topup\n\nprotocol TopupInteractable: Interactable, AddPaymentMethodListener, EnterAmountListener, CardOnFileListener {\n  var router: TopupRouting? { get set }\n  var listener: TopupListener? { get set }\n  var presentationDelegateProxy: AdaptivePresentationControllerDelegateProxy { get }\n}\n\nprotocol TopupViewControllable: ViewControllable {\n}\n\nfinal class TopupRouter: Router<TopupInteractable>, TopupRouting {\n  \n  private var navigationControllable: NavigationControllerable?\n  \n  private let addPaymentMethodBuildable: AddPaymentMethodBuildable\n  private var addPaymentMethodRouting: Routing?\n  \n  private let enterAmountBuildable: EnterAmountBuildable\n  private var enterAmountRouting: Routing?\n  \n  private let cardOnFileBuildable: CardOnFileBuildable\n  private var cardOnFileRouting: Routing?\n  \n  init(\n    interactor: TopupInteractable,\n    viewController: ViewControllable,\n    addPaymentMethodBuildable: AddPaymentMethodBuildable,\n    enterAmountBuildable: EnterAmountBuildable,\n    cardOnFileBuildable: CardOnFileBuildable\n  ) {\n    self.viewController = viewController\n    self.addPaymentMethodBuildable = addPaymentMethodBuildable\n    self.enterAmountBuildable = enterAmountBuildable\n    self.cardOnFileBuildable = cardOnFileBuildable\n    super.init(interactor: interactor)\n    interactor.router = self\n  }\n  \n  func cleanupViews() {\n    if viewController.uiviewController.presentedViewController != nil, navigationControllable != nil {\n      navigationControllable?.dismiss(completion: nil)\n    }\n  }\n  \n  func attachAddPaymentMethod(closeButtonType: DismissButtonType) {\n    if addPaymentMethodRouting != nil {\n      return\n    }\n    \n    let router = addPaymentMethodBuildable.build(withListener: interactor, closeButtonType: closeButtonType)\n    \n    if let navigationControllable = navigationControllable {\n      navigationControllable.pushViewController(router.viewControllable, animated: true)\n    } else {\n      presentInsideNavigation(router.viewControllable)\n    }\n    \n    attachChild(router)\n    addPaymentMethodRouting = router\n  }\n  \n  func detachAddPaymentMethod() {\n    guard let router = addPaymentMethodRouting else {\n      return\n    }\n    \n    navigationControllable?.popViewController(animated: true)\n    detachChild(router)\n    addPaymentMethodRouting = nil\n  }\n  \n  func attachEnterAmount() {\n    if enterAmountRouting != nil {\n      return\n    }\n    \n    let router = enterAmountBuildable.build(withListener: interactor)\n    \n    if let navigation = navigationControllable {\n      navigation.setViewControllers([router.viewControllable])\n      resetChildRouting()\n    } else {\n      presentInsideNavigation(router.viewControllable)\n    }\n    \n    attachChild(router)\n    enterAmountRouting = router\n  }\n  \n  func detachEnterAmount() {\n    guard let router = enterAmountRouting else {\n      return\n    }\n    \n    dismissPresentedNavigation(completion: nil)\n    detachChild(router)\n    enterAmountRouting = nil\n  }\n  \n  func attachCardOnFile(paymentMethods: [PaymentMethod]) {\n    if cardOnFileRouting != nil {\n      return\n    }\n    \n    let router = cardOnFileBuildable.build(withListener: interactor, paymentMethods: paymentMethods)\n    navigationControllable?.pushViewController(router.viewControllable, animated: true)\n    cardOnFileRouting = router\n    attachChild(router)\n  }\n  \n  func detachCardOnFile() {\n    guard let router = cardOnFileRouting else {\n      return\n    }\n    \n    navigationControllable?.popViewController(animated: true)\n    detachChild(router)\n    cardOnFileRouting = nil\n  }\n  \n  func popToRoot() {\n    navigationControllable?.popToRoot(animated: true)\n    resetChildRouting()\n  }\n  \n  private func presentInsideNavigation(_ viewControllable: ViewControllable) {\n    let navigation = NavigationControllerable(root: viewControllable)\n    navigation.navigationController.presentationController?.delegate = interactor.presentationDelegateProxy\n    self.navigationControllable = navigation\n    viewController.present(navigation, animated: true, completion: nil)\n  }\n  \n  private func dismissPresentedNavigation(completion: (() -> Void)?) {\n    if self.navigationControllable == nil {\n      return\n    }\n    \n    viewController.dismiss(completion: nil)\n    self.navigationControllable = nil\n  }\n  \n  private func resetChildRouting() {\n    if let cardOnFileRouting = cardOnFileRouting {\n      detachChild(cardOnFileRouting)\n      self.cardOnFileRouting = nil\n    }\n    \n    if let addPaymentMethodRouting = addPaymentMethodRouting {\n      detachChild(addPaymentMethodRouting)\n      self.addPaymentMethodRouting = nil\n    }\n  }\n  \n  // MARK: - Private\n  \n  private let viewController: ViewControllable\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Sources/TopupTestSupport/TopupMock.swift",
    "content": "import Foundation\nimport Topup\n\npublic final class TopupListenerMock: TopupListener {\n  \n  public var topupDidCloseCallCount = 0\n  public func topupDidClose() {\n    topupDidCloseCallCount += 1\n  }\n  \n  public var topupDidFinishCallCount = 0\n  public func topupDidFinish() {\n    topupDidFinishCallCount += 1\n  }\n  \n  public init() {\n    \n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/CardOnFile/CardOnFileMock.swift",
    "content": "@testable import TopupImp\nimport Foundation\nimport RIBsTestSupport\nimport FinanceEntity\n\nfinal class CardOnFileBuildableMock: CardOnFileBuildable {\n  \n  var buildHandler: ((_ listener: CardOnFileListener) -> CardOnFileRouting)?\n  \n  var buildCallCount = 0\n  var buildPaymentMethods: [PaymentMethod]?\n  func build(withListener listener: CardOnFileListener, paymentMethods: [PaymentMethod]) -> CardOnFileRouting {\n    buildCallCount += 1\n    buildPaymentMethods = paymentMethods\n    \n    if let buildHandler = buildHandler {\n      return buildHandler(listener)\n    }\n    \n    fatalError()\n  }\n  \n}\n\nfinal class CardOnFileRoutingMock: ViewableRoutingMock, CardOnFileRouting {\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/CardOnFile/CardOnFileViewTests.swift",
    "content": "@testable import TopupImp\nimport XCTest\nimport Foundation\nimport SnapshotTesting\nimport FinanceEntity\n\nfinal class CardOnFileViewTests: XCTestCase {\n  \n  func testCardOnFile() {\n    // given\n    let viewModels = [\n      PaymentMethodViewModel(PaymentMethod(id: \"0\", name: \"우리은행\", digits: \"1111\", color: \"#3478f6ff\", isPrimary: false)),\n      PaymentMethodViewModel(PaymentMethod(id: \"1\", name: \"현대카드\", digits: \"2222\", color: \"#f19a38ff\", isPrimary: false)),\n      PaymentMethodViewModel(PaymentMethod(id: \"2\", name: \"신한카드\", digits: \"3333\", color: \"#78c5f5ff\", isPrimary: false)),\n    ]\n    \n    // when\n    let sut = CardOnFileViewController()\n    sut.update(with: viewModels)\n    \n    // then\n    assertSnapshot(matching: sut, as: .image(on: .iPhoneXsMax))\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/EnterAmount/EnterAmountInteractorTests.swift",
    "content": "@testable import TopupImp\nimport XCTest\nimport FinanceEntity\nimport FinanceRepositoryTestSupport\n\nfinal class EnterAmountInteractorTests: XCTestCase {\n  \n  private var sut: EnterAmountInteractor!\n  private var presenter: EnterAmountPresentableMock!\n  private var dependency: EnterAmountDependencyMock!\n  private var listener: EnterAmountListenerMock!\n  \n  private var repository: SuperPayRepositoryMock! {\n    dependency.superPayRepository as? SuperPayRepositoryMock\n  }\n  \n  override func setUp() {\n    super.setUp()\n    \n    self.presenter = EnterAmountPresentableMock()\n    self.dependency = EnterAmountDependencyMock()\n    self.listener = EnterAmountListenerMock()\n    \n    sut = EnterAmountInteractor(\n      presenter: self.presenter,\n      dependency: self.dependency\n    )\n    sut.listener = self.listener\n  }\n  \n  // MARK: - Tests\n  func testActivate() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"id_0\",\n      name: \"name_0\",\n      digits: \"9999\",\n      color: \"#13ABE8FF\",\n      isPrimary: false\n    )\n    dependency.selectedPaymentMethodSubject.send(paymentMethod)\n    \n    // when\n    sut.activate()\n    \n    // then\n    XCTAssertEqual(presenter.updateSelectedPaymentMethodCallCount, 1)\n    XCTAssertEqual(presenter.updateSelectedPaymentMethodViewModel?.name, \"name_0 9999\")\n    XCTAssertNotNil(presenter.updateSelectedPaymentMethodViewModel?.image)\n  }\n  \n  func testTopupWithValidAmount() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"id_0\",\n      name: \"name_0\",\n      digits: \"9999\",\n      color: \"#13ABE8FF\",\n      isPrimary: false\n    )\n    dependency.selectedPaymentMethodSubject.send(paymentMethod)\n    \n    // when\n    sut.didTapTopup(with: 1_000_000)\n    \n    // then\n    XCTAssertEqual(presenter.startLoadingCallCount, 1)\n    XCTAssertEqual(presenter.stopLoadingCallCount, 1)\n    XCTAssertEqual(repository.topupCallCount, 1)\n    XCTAssertEqual(repository.paymentMethodID, \"id_0\")\n    XCTAssertEqual(repository.topupAmount, 1_000_000)\n    XCTAssertEqual(listener.enterAmountDidFinishTopupCallCount, 1)\n  }\n  \n  func testTopupWithFailure() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"id_0\",\n      name: \"name_0\",\n      digits: \"9999\",\n      color: \"#13ABE8FF\",\n      isPrimary: false\n    )\n    dependency.selectedPaymentMethodSubject.send(paymentMethod)\n    repository.shouldTopupSucceed = false\n    \n    // when\n    sut.didTapTopup(with: 1_000_000)\n    \n    // then\n    XCTAssertEqual(presenter.startLoadingCallCount, 1)\n    XCTAssertEqual(presenter.stopLoadingCallCount, 1)\n    XCTAssertEqual(listener.enterAmountDidFinishTopupCallCount, 0)\n  }\n  \n  func testDidTapClose() {\n    // given\n    \n    // when\n    sut.didTapClose()\n    \n    // then\n    XCTAssertEqual(listener.enterAmountDidTapCloseCallCount, 1)\n  }\n  \n  func testDidTapPaymentMethod() {\n    // given\n    \n    // when\n    sut.didTapPaymentMethod()\n    \n    // then\n    XCTAssertEqual(listener.enterAmountDidTapPaymentMethodCallCount, 1)\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/EnterAmount/EnterAmountMock.swift",
    "content": "@testable import TopupImp\nimport Foundation\nimport CombineUtil\nimport FinanceEntity\nimport FinanceRepository\nimport FinanceRepositoryTestSupport\nimport CombineSchedulers\nimport RIBsTestSupport\n\nfinal class EnterAmountPresentableMock: EnterAmountPresentable {\n  \n  var listener: EnterAmountPresentableListener?\n\n  var updateSelectedPaymentMethodCallCount = 0\n  var updateSelectedPaymentMethodViewModel: SelectedPaymentMethodViewModel?\n  func updateSelectedPaymentMethod(with viewModel: SelectedPaymentMethodViewModel) {\n    updateSelectedPaymentMethodCallCount += 1\n    updateSelectedPaymentMethodViewModel = viewModel\n  }\n  \n  var startLoadingCallCount = 0\n  func startLoading() {\n    startLoadingCallCount += 1\n  }\n  \n  var stopLoadingCallCount = 0\n  func stopLoading() {\n    stopLoadingCallCount += 1\n  }\n  \n  init() {\n    \n  }\n}\n\nfinal class EnterAmountDependencyMock: EnterAmountInteractorDependency {\n  var mainQueue: AnySchedulerOf<DispatchQueue> { .immediate }\n  \n  var selectedPaymentMethodSubject = CurrentValuePublisher<PaymentMethod>(\n    PaymentMethod(\n      id: \"\",\n      name: \"\",\n      digits: \"\",\n      color: \"\",\n      isPrimary: false\n    )\n  )\n  \n  var selectedPaymentMethod: ReadOnlyCurrentValuePublisher<PaymentMethod> { selectedPaymentMethodSubject }\n  var superPayRepository: SuperPayRepository = SuperPayRepositoryMock()\n}\n\nfinal class EnterAmountListenerMock: EnterAmountListener {\n  \n  var enterAmountDidTapCloseCallCount = 0\n  func enterAmountDidTapClose() {\n    enterAmountDidTapCloseCallCount += 1\n  }\n  \n  var enterAmountDidTapPaymentMethodCallCount = 0\n  func enterAmountDidTapPaymentMethod() {\n    enterAmountDidTapPaymentMethodCallCount += 1\n  }\n  \n  var enterAmountDidFinishTopupCallCount = 0\n  func enterAmountDidFinishTopup() {\n    enterAmountDidFinishTopupCallCount += 1\n  }\n  \n}\n\nfinal class EnterAmountBuildableMock: EnterAmountBuildable {\n  \n  var buildHandler: ((_ listener: EnterAmountListener) -> EnterAmountRouting)?\n  \n  var buildCallCount = 0\n  func build(withListener listener: EnterAmountListener) -> EnterAmountRouting {\n    buildCallCount += 1\n    \n    if let buildHandler = buildHandler {\n      return buildHandler(listener)\n    }\n    \n    fatalError()\n  }\n}\n\nfinal class EnterAmountRoutingMock: ViewableRoutingMock, EnterAmountRouting {\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/EnterAmount/EnterAmountRouterTests.swift",
    "content": "@testable import TopupImp\nimport XCTest\n\nfinal class EnterAmountRouterTests: XCTestCase {\n  \n  private var router: EnterAmountRouter!\n  \n  override func setUp() {\n    super.setUp()\n    \n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/EnterAmount/EnterAmountViewTests.swift",
    "content": "import XCTest\nimport Foundation\n@testable import TopupImp\nimport SnapshotTesting\nimport FinanceEntity\n\nfinal class EnterAmountViewTests: XCTestCase {\n  \n  func testEnterAmount() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"0\",\n      name: \"슈퍼은행\",\n      digits: \"**** 9999\",\n      color: \"#51AF80FF\",\n      isPrimary: false\n    )\n    let viewModel = SelectedPaymentMethodViewModel(paymentMethod)\n    \n    // when\n    let sut = EnterAmountViewController()\n    sut.updateSelectedPaymentMethod(with: viewModel)\n    \n    // then\n    assertSnapshot(matching: sut, as: .image(on: .iPhoneXsMax))\n  }\n  \n  func testEnterAmountLoading() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"0\",\n      name: \"슈퍼은행\",\n      digits: \"**** 9999\",\n      color: \"#51AF80FF\",\n      isPrimary: false\n    )\n    let viewModel = SelectedPaymentMethodViewModel(paymentMethod)\n    \n    // when\n    let sut = EnterAmountViewController()\n    sut.updateSelectedPaymentMethod(with: viewModel)\n    sut.startLoading()\n    \n    // then\n    assertSnapshot(matching: sut, as: .image(on: .iPhoneXsMax))\n  }\n  \n  func testEnterAmountStopLoading() {\n    // given\n    let paymentMethod = PaymentMethod(\n      id: \"0\",\n      name: \"슈퍼은행\",\n      digits: \"**** 9999\",\n      color: \"#51AF80FF\",\n      isPrimary: false\n    )\n    let viewModel = SelectedPaymentMethodViewModel(paymentMethod)\n    \n    // when\n    let sut = EnterAmountViewController()\n    sut.updateSelectedPaymentMethod(with: viewModel)\n    sut.startLoading()\n    sut.stopLoading()\n    \n    // then\n    assertSnapshot(matching: sut, as: .image(on: .iPhoneXsMax))\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/Topup/TopupInteractorTests.swift",
    "content": "@testable import TopupImp\nimport XCTest\nimport TopupTestSupport\nimport FinanceEntity\nimport FinanceRepositoryTestSupport\n\nfinal class TopupInteractorTests: XCTestCase {\n  \n  private var sut: TopupInteractor!\n  private var dependency: TopupDependencyMock!\n  private var listener: TopupListenerMock!\n  private var router: TopupRoutingMock!\n\n  private var cardOnFileRepository: CardOnFileRepositoryMock {\n    dependency.cardOnFileRepository as! CardOnFileRepositoryMock\n  }\n  \n  override func setUp() {\n    super.setUp()\n    \n    self.dependency = TopupDependencyMock()\n    self.listener = TopupListenerMock()\n    \n    let interactor = TopupInteractor(dependency: self.dependency)\n    self.router = TopupRoutingMock(interactable: interactor)\n    \n    interactor.listener = self.listener\n    interactor.router = self.router\n    self.sut = interactor\n  }\n  \n  // MARK: - Tests\n  \n  func testActivate() {\n    // given\n    let cards = [\n      PaymentMethod(\n        id: \"0\",\n        name: \"Zero\",\n        digits: \"0123\",\n        color: \"\",\n        isPrimary: false\n      )\n    ]\n    cardOnFileRepository.cardOnFileSubject.send(cards)\n    \n    // when\n    sut.activate()\n    \n    // then\n    XCTAssertEqual(router.attachEnterAmountCallCount, 1)\n    XCTAssertEqual(dependency.paymentMethodStream.value.name, \"Zero\")\n  }\n  \n  func testActivateWithoutCard() {\n    // given\n    cardOnFileRepository.cardOnFileSubject.send([])\n    \n    // when\n    sut.activate()\n    \n    // then\n    XCTAssertEqual(router.attachAddPaymentMethodCallCount, 1)\n    XCTAssertEqual(router.attachAddPaymentMethodCloseButtonType, .close)\n  }\n  \n  func testDidAddCardWithCard() {\n    // given\n    let cards = [\n      PaymentMethod(\n        id: \"0\",\n        name: \"Zero\",\n        digits: \"0123\",\n        color: \"\",\n        isPrimary: false\n      )\n    ]\n    cardOnFileRepository.cardOnFileSubject.send(cards)\n    \n    let newCard = PaymentMethod(\n      id: \"new_card_id\",\n      name: \"New Card\",\n      digits: \"0000\",\n      color: \"\",\n      isPrimary: false\n    )\n    \n    // when\n    sut.activate()\n    sut.addPaymentMethodDidAddCard(paymentMethod: newCard)\n    \n    // then\n    XCTAssertEqual(router.popToRootCallCount, 1)\n    XCTAssertEqual(dependency.paymentMethodStream.value.id, \"new_card_id\")\n  }\n  \n  func testDidAddCardWithoutCard() {\n    // given\n    cardOnFileRepository.cardOnFileSubject.send([])\n    \n    let newCard = PaymentMethod(\n      id: \"new_card_id\",\n      name: \"New Card\",\n      digits: \"0000\",\n      color: \"\",\n      isPrimary: false\n    )\n    \n    // when\n    sut.activate()\n    sut.addPaymentMethodDidAddCard(paymentMethod: newCard)\n    \n    // then\n    XCTAssertEqual(router.attachEnterAmountCallCount, 1)\n    XCTAssertEqual(dependency.paymentMethodStream.value.id, \"new_card_id\")\n  }\n  \n  func testAddPaymentMethodDidTapCloseFromEnterAmount() {\n    // given\n    let cards = [\n      PaymentMethod(\n        id: \"0\",\n        name: \"Zero\",\n        digits: \"0123\",\n        color: \"\",\n        isPrimary: false\n      )\n    ]\n    cardOnFileRepository.cardOnFileSubject.send(cards)\n    \n    // when\n    sut.activate()\n    sut.addPaymentMethodDidTapClose()\n    \n    // then\n    XCTAssertEqual(router.detachAddPaymentMethodCallCount, 1)\n  }\n  \n  func testAddPaymentMethodDidTapClose() {\n    // given\n    cardOnFileRepository.cardOnFileSubject.send([])\n    \n    // when\n    sut.activate()\n    sut.addPaymentMethodDidTapClose()\n    \n    // then\n    XCTAssertEqual(router.detachAddPaymentMethodCallCount, 1)\n    XCTAssertEqual(listener.topupDidCloseCallCount, 1)\n  }\n  \n  func testDidSelectCard() {\n    // given\n    let cards = [\n      PaymentMethod(\n        id: \"0\",\n        name: \"Zero\",\n        digits: \"0123\",\n        color: \"\",\n        isPrimary: false\n      ),\n      PaymentMethod(\n        id: \"1\",\n        name: \"One\",\n        digits: \"1234\",\n        color: \"\",\n        isPrimary: false\n      )\n    ]\n    cardOnFileRepository.cardOnFileSubject.send(cards)\n    \n    // when\n    sut.cardOnFileDidSelect(at: 0)\n    \n    // then\n    XCTAssertEqual(dependency.paymentMethodStream.value.id, \"0\")\n    XCTAssertEqual(router.detachCardOnFileCallCount, 1)\n  }\n  \n  func testDidSelectCardWithInvalidIndex() {\n    // given\n    let cards = [\n      PaymentMethod(\n        id: \"0\",\n        name: \"Zero\",\n        digits: \"0123\",\n        color: \"\",\n        isPrimary: false\n      ),\n      PaymentMethod(\n        id: \"1\",\n        name: \"One\",\n        digits: \"1234\",\n        color: \"\",\n        isPrimary: false\n      )\n    ]\n    cardOnFileRepository.cardOnFileSubject.send(cards)\n    \n    // when\n    sut.cardOnFileDidSelect(at: 2)\n    \n    // then\n    XCTAssertEqual(dependency.paymentMethodStream.value.id, \"\")\n    XCTAssertEqual(router.detachCardOnFileCallCount, 1)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/Topup/TopupMock.swift",
    "content": "import Foundation\nimport FinanceRepository\nimport FinanceRepositoryTestSupport\nimport CombineUtil\nimport FinanceEntity\nimport RIBsUtil\nimport ModernRIBs\nimport Combine\nimport Topup\nimport SuperUI\n@testable import TopupImp\n\nfinal class TopupDependencyMock: TopupInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository = CardOnFileRepositoryMock()\n  var paymentMethodStream: CurrentValuePublisher<PaymentMethod> = .init(\n  PaymentMethod(id: \"\", name: \"\", digits: \"\", color: \"\", isPrimary: false)\n  )\n}\n\nfinal class TopupRoutingMock: TopupRouting {\n  \n  var attachAddPaymentMethodCallCount = 0\n  var attachAddPaymentMethodCloseButtonType: DismissButtonType?\n  func attachAddPaymentMethod(closeButtonType: DismissButtonType) {\n    attachAddPaymentMethodCallCount += 1\n    attachAddPaymentMethodCloseButtonType = closeButtonType\n  }\n  \n  var detachAddPaymentMethodCallCount = 0\n  func detachAddPaymentMethod() {\n    detachAddPaymentMethodCallCount += 1\n  }\n  \n  var attachEnterAmountCallCount = 0\n  func attachEnterAmount() {\n    attachEnterAmountCallCount += 1\n  }\n  \n  var detachEnterAmountCallCount = 0\n  func detachEnterAmount() {\n    detachEnterAmountCallCount += 1\n  }\n  \n  var attachCardOnFileCallCount = 0\n  var attachCardOnFileCallCountPaymentMethods: [PaymentMethod]?\n  func attachCardOnFile(paymentMethods: [PaymentMethod]) {\n    attachCardOnFileCallCount += 1\n  }\n  \n  var detachCardOnFileCallCount = 0\n  func detachCardOnFile() {\n    detachCardOnFileCallCount += 1\n  }\n  \n  var popToRootCallCount = 0\n  func popToRoot() {\n    popToRootCallCount += 1\n  }\n  \n  // Variables\n  var interactable: Interactable { didSet { interactableSetCallCount += 1 } }\n  var interactableSetCallCount = 0\n  var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  var childrenSetCallCount = 0\n  var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  var lifecycleSubjectSetCallCount = 0\n  var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n  \n  // Function Handlers\n  var loadHandler: (() -> ())?\n  var loadCallCount: Int = 0\n  var attachChildHandler: ((_ child: Routing) -> ())?\n  var attachChildCallCount: Int = 0\n  var detachChildHandler: ((_ child: Routing) -> ())?\n  var detachChildCallCount: Int = 0\n  \n  init(\n    interactable: Interactable\n  ) {\n    self.interactable = interactable\n  }\n  \n  var cleanupViewsCallCount = 0\n  func cleanupViews() {\n    cleanupViewsCallCount += 1\n  }\n  \n  func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n  \n  func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n  \n  func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n}\n\nfinal class TopupInteractableMock: TopupInteractable {\n  \n  var router: TopupRouting?\n  var listener: TopupListener?\n  var presentationDelegateProxy = AdaptivePresentationControllerDelegateProxy()\n  \n  var addPaymentMethodDidTapCloseCallCount = 0\n  func addPaymentMethodDidTapClose() {\n    addPaymentMethodDidTapCloseCallCount += 1\n  }\n  \n  var addPaymentMethodDidAddCardCallCount = 0\n  var addPaymentMethodDidAddCardPaymentMethod: PaymentMethod?\n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    addPaymentMethodDidAddCardCallCount += 1\n    addPaymentMethodDidAddCardPaymentMethod = paymentMethod\n  }\n  \n  var enterAmountDidTapCloseCallCount = 0\n  func enterAmountDidTapClose() {\n    enterAmountDidTapCloseCallCount += 1\n  }\n  \n  var enterAmountDidTapPaymentMethodCallCount = 0\n  func enterAmountDidTapPaymentMethod() {\n    enterAmountDidTapPaymentMethodCallCount += 1\n  }\n  \n  var enterAmountDidFinishTopupCallCount = 0\n  func enterAmountDidFinishTopup() {\n    enterAmountDidFinishTopupCallCount += 1\n  }\n  \n  var cardOnFileDidTapCloseCallCount = 0\n  func cardOnFileDidTapClose() {\n    cardOnFileDidTapCloseCallCount += 1\n  }\n  \n  var cardOnFileDidTapAddCardCallCount = 0\n  func cardOnFileDidTapAddCard() {\n    cardOnFileDidTapAddCardCallCount += 1\n  }\n  \n  var cardOnFileDidSelectCallCount = 0\n  var cardOnFileDidSelectIndex: Int?\n  func cardOnFileDidSelect(at index: Int) {\n    cardOnFileDidSelectCallCount += 1\n    cardOnFileDidSelectIndex = index\n  }\n  \n  func activate() {\n    \n  }\n  \n  func deactivate() {\n    \n  }\n  \n  var isActive: Bool { isActiveSubject.value }\n  var isActiveStream: AnyPublisher<Bool, Never> { isActiveSubject.eraseToAnyPublisher() }\n  private let isActiveSubject = CurrentValueSubject<Bool, Never>(false)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Finance/Tests/TopupImpTests/Topup/TopupRouterTests.swift",
    "content": "@testable import TopupImp\nimport XCTest\nimport RIBsTestSupport\nimport AddPaymentMethodTestSupport\nimport ModernRIBs\n\nfinal class TopupRouterTests: XCTestCase {\n  \n  private var sut: TopupRouter!\n  private var interactor: TopupInteractableMock!\n  private var viewController: ViewControllableMock!\n  private var addPaymentMethodBuildable: AddPaymentMethodBuildableMock!\n  private var enterAmountBuildable: EnterAmountBuildableMock!\n  private var cardOnFileBuildable: CardOnFileBuildableMock!\n  \n  override func setUp() {\n    super.setUp()\n    \n    interactor = TopupInteractableMock()\n    viewController = ViewControllableMock()\n    addPaymentMethodBuildable = AddPaymentMethodBuildableMock()\n    enterAmountBuildable = EnterAmountBuildableMock()\n    cardOnFileBuildable = CardOnFileBuildableMock()\n    \n    sut = TopupRouter(\n      interactor: interactor,\n      viewController: viewController,\n      addPaymentMethodBuildable: addPaymentMethodBuildable,\n      enterAmountBuildable: enterAmountBuildable,\n      cardOnFileBuildable: cardOnFileBuildable\n    )\n  }\n  \n  // MARK: - Tests\n  \n  func testAttachAddPaymentMethod() {\n    // given\n    \n    // when\n    sut.attachAddPaymentMethod(closeButtonType: .close)\n    \n    // then\n    XCTAssertEqual(addPaymentMethodBuildable.buildCallCount, 1)\n    XCTAssertEqual(addPaymentMethodBuildable.closeButtonType, .close)\n    XCTAssertEqual(viewController.presentCallCount, 1)\n  }\n  \n  func testAttachEnterAmount() {\n    // given\n    let router = EnterAmountRoutingMock(\n      interactable: Interactor(),\n      viewControllable: ViewControllableMock()\n    )\n    \n    var assignedListener: EnterAmountListener?\n    enterAmountBuildable.buildHandler = { listener in\n      assignedListener = listener\n      return router\n    }\n    \n    // when\n    sut.attachEnterAmount()\n    \n    // then\n    XCTAssertTrue(assignedListener === interactor)\n    XCTAssertEqual(enterAmountBuildable.buildCallCount, 1)\n  }\n  \n  func testAttachEnterAmountOnNavigation() {\n    // given\n    let router = EnterAmountRoutingMock(\n      interactable: Interactor(),\n      viewControllable: ViewControllableMock()\n    )\n    \n    var assignedListener: EnterAmountListener?\n    enterAmountBuildable.buildHandler = { listener in\n      assignedListener = listener\n      return router\n    }\n    \n    // when\n    sut.attachAddPaymentMethod(closeButtonType: .close)\n    sut.attachEnterAmount()\n    \n    // then\n    XCTAssertTrue(assignedListener === interactor)\n    XCTAssertEqual(enterAmountBuildable.buildCallCount, 1)\n    XCTAssertEqual(viewController.presentCallCount, 1)\n    XCTAssertEqual(sut.children.count, 1)\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppDelegate/AppComponent.swift",
    "content": "import Foundation\nimport ModernRIBs\n\nfinal class AppComponent: Component<EmptyDependency>, AppRootDependency {\n  \n  init() {\n    super.init(dependency: EmptyComponent())\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppDelegate/AppDelegate.swift",
    "content": "import UIKit\nimport ModernRIBs\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  \n  var window: UIWindow?\n  \n  private var launchRouter: LaunchRouting?\n  private var urlHandler: URLHandler?\n  \n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    \n    let window = UIWindow(frame: UIScreen.main.bounds)\n    self.window = window\n    \n    let result = AppRootBuilder(dependency: AppComponent()).build()\n    self.launchRouter = result.launchRouter\n    self.urlHandler = result.urlHandler\n    launchRouter?.launch(from: window)\n    \n    return true\n  }\n  \n}\n\nprotocol URLHandler: AnyObject {\n  func handle(_ url: URL)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/AppRootBuilder.swift",
    "content": "import ModernRIBs\nimport UIKit\nimport FinanceRepository\nimport FinanceHome\nimport AppHome\nimport ProfileHome\n\nprotocol AppRootDependency: Dependency {\n}\n\n// MARK: - Builder\n\nprotocol AppRootBuildable: Buildable {\n  func build() -> (launchRouter: LaunchRouting, urlHandler: URLHandler)\n}\n\nfinal class AppRootBuilder: Builder<AppRootDependency>, AppRootBuildable {\n  \n  override init(dependency: AppRootDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  func build() -> (launchRouter: LaunchRouting, urlHandler: URLHandler) {\n    let tabBar = RootTabBarController()\n    \n    let component = AppRootComponent(\n      dependency: dependency,\n      rootViewController: tabBar\n    )\n    \n    let interactor = AppRootInteractor(presenter: tabBar)\n    \n    let appHome = AppHomeBuilder(dependency: component)\n    let financeHome = FinanceHomeBuilder(dependency: component)\n    let profileHome = ProfileHomeBuilder(dependency: component)\n    let router = AppRootRouter(\n      interactor: interactor,\n      viewController: tabBar,\n      appHome: appHome,\n      financeHome: financeHome,\n      profileHome: profileHome\n    )\n    \n    return (router, interactor)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/AppRootComponent.swift",
    "content": "import Foundation\nimport AppHome\nimport FinanceHome\nimport ProfileHome\nimport FinanceRepository\nimport ModernRIBs\nimport TransportHome\nimport TransportHomeImp\nimport Topup\nimport TopupImp\nimport AddPaymentMethod\nimport AddPaymentMethodImp\nimport Network\nimport NetworkImp\nimport CombineSchedulers\n\nfinal class AppRootComponent: Component<AppRootDependency>, AppHomeDependency, FinanceHomeDependency, ProfileHomeDependency, TransportHomeDependency, TopupDependency, AddPaymentMethodDependency {\n  \n  var cardOnFileRepository: CardOnFileRepository\n  var superPayRepository: SuperPayRepository\n  var mainQueue: AnySchedulerOf<DispatchQueue> { .main }\n  \n  lazy var transportHomeBuildable: TransportHomeBuildable = {\n    return TransportHomeBuilder(dependency: self)\n  }()\n  \n  lazy var topupBuildable: TopupBuildable = {\n    return TopupBuilder(dependency: self)\n  }()\n  \n  lazy var addPaymentMethodBuildable: AddPaymentMethodBuildable = {\n    return AddPaymentMethodBuilder(dependency: self)\n  }()\n  \n  var topupBaseViewController: ViewControllable { rootViewController.topViewControllable }\n  \n  private let rootViewController: ViewControllable\n  \n  init(\n    dependency: AppRootDependency,\n    rootViewController: ViewControllable\n  ) {\n    #if UITESTING\n    let config = URLSessionConfiguration.default\n    #else\n    let config = URLSessionConfiguration.ephemeral\n    config.protocolClasses = [SuperAppURLProtocol.self]\n    setupURLProtocol()\n    #endif\n    \n    let network = NetworkImp(session: URLSession(configuration: config))\n    \n    self.cardOnFileRepository = CardOnFileRepositoryImp(network: network, baseURL: BaseURL().financeBaseURL)\n    self.cardOnFileRepository.fetch()\n    \n    self.superPayRepository = SuperPayRepositoryImp(network: network, baseURL: BaseURL().financeBaseURL)\n    self.rootViewController = rootViewController\n    super.init(dependency: dependency)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/AppRootInteractor.swift",
    "content": "import Foundation\nimport ModernRIBs\n\nprotocol AppRootRouting: ViewableRouting {\n  func attachTabs()\n}\n\nprotocol AppRootPresentable: Presentable {\n  var listener: AppRootPresentableListener? { get set }\n}\n\nprotocol AppRootListener: AnyObject {\n}\n\nfinal class AppRootInteractor: PresentableInteractor<AppRootPresentable>, AppRootInteractable, AppRootPresentableListener, URLHandler {\n  \n  weak var router: AppRootRouting?\n  weak var listener: AppRootListener?\n  \n  override init(presenter: AppRootPresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    router?.attachTabs()\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n  \n  func handle(_ url: URL) {\n    \n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/AppRootRouter.swift",
    "content": "import ModernRIBs\nimport RIBsUtil\nimport FinanceHome\nimport AppHome\nimport ProfileHome\n\nprotocol AppRootInteractable: Interactable,\n                              AppHomeListener,\n                              FinanceHomeListener,\n                              ProfileHomeListener {\n  var router: AppRootRouting? { get set }\n  var listener: AppRootListener? { get set }\n}\n\nprotocol AppRootViewControllable: ViewControllable {\n  func setViewControllers(_ viewControllers: [ViewControllable])\n}\n\nfinal class AppRootRouter: LaunchRouter<AppRootInteractable, AppRootViewControllable>, AppRootRouting {\n  \n  private let appHome: AppHomeBuildable\n  private let financeHome: FinanceHomeBuildable\n  private let profileHome: ProfileHomeBuildable\n  \n  private var appHomeRouting: ViewableRouting?\n  private var financeHomeRouting: ViewableRouting?\n  private var profileHomeRouting: ViewableRouting?\n  \n  init(\n    interactor: AppRootInteractable,\n    viewController: AppRootViewControllable,\n    appHome: AppHomeBuildable,\n    financeHome: FinanceHomeBuildable,\n    profileHome: ProfileHomeBuildable\n  ) {\n    self.appHome = appHome\n    self.financeHome = financeHome\n    self.profileHome = profileHome\n    \n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachTabs() {\n    let appHomeRouting = appHome.build(withListener: interactor)\n    let financeHomeRouting = financeHome.build(withListener: interactor)\n    let profileHomeRouting = profileHome.build(withListener: interactor)\n    \n    attachChild(appHomeRouting)\n    attachChild(financeHomeRouting)\n    attachChild(profileHomeRouting)\n    \n    let viewControllers = [\n      NavigationControllerable(root: appHomeRouting.viewControllable),\n      NavigationControllerable(root: financeHomeRouting.viewControllable),\n      profileHomeRouting.viewControllable\n    ]\n    \n    viewController.setViewControllers(viewControllers)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/BaseURL.swift",
    "content": "import Foundation\n\nstruct BaseURL {\n  var financeBaseURL: URL {\n    #if UITESTING\n    return URL(string: \"http://localhost:8080\")!\n    #else\n    return URL(string: \"https://finance.superapp.com/api/v1\")!\n    #endif\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/RootTabBarController.swift",
    "content": "import UIKit\nimport ModernRIBs\n\nprotocol AppRootPresentableListener: AnyObject {\n  \n}\n\nfinal class RootTabBarController: UITabBarController, AppRootViewControllable, AppRootPresentable {\n  weak var listener: AppRootPresentableListener?\n  \n  override func viewDidLoad() {\n    super.viewDidLoad()\n    \n    tabBar.isTranslucent = false\n    tabBar.tintColor = .black\n    tabBar.backgroundColor = .white\n  }\n  \n  func setViewControllers(_ viewControllers: [ViewControllable]) {\n    super.setViewControllers(viewControllers.map(\\.uiviewController), animated: false)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/SetupURLProtocol.swift",
    "content": "import Foundation\n\nfunc setupURLProtocol() {\n  // Topup\n  let topupResponse: [String: Any] = [\n    \"status\": \"success\"\n  ]\n  \n  let topupResponseData = try! JSONSerialization.data(withJSONObject: topupResponse, options: [])\n  \n  // AddCard\n  let addCardResponse: [String: Any] = [\n    \"card\": [\n      \"id\": \"999\",\n      \"name\": \"새 카드\",\n      \"digits\": \"**** 0101\",\n      \"color\": \"\",\n      \"isPrimary\": false\n    ]\n  ]\n  \n  let addCardResponseData = try! JSONSerialization.data(withJSONObject: addCardResponse, options: [])\n  \n  // CardOnFile\n  let cardOnFileResponse: [String: Any] = [\n    \"cards\": [\n      [\n        \"id\": \"0\",\n        \"name\": \"우리은행\",\n        \"digits\": \"0123\",\n        \"color\": \"#f19a38ff\",\n        \"isPrimary\": false\n      ],\n      [\n        \"id\": \"1\",\n        \"name\": \"신한카드\",\n        \"digits\": \"0987\",\n        \"color\": \"#3478f6ff\",\n        \"isPrimary\": false\n      ],\n//      [\n//        \"id\": \"3\",\n//        \"name\": \"국민은행\",\n//        \"digits\": \"2812\",\n//        \"color\": \"#65c466ff\",\n//        \"isPrimary\": false\n//      ]\n    ]\n  ]\n  \n  let cardOnFileResponseData = try! JSONSerialization.data(withJSONObject: cardOnFileResponse, options: [])\n  \n  SuperAppURLProtocol.successMock = [\n    \"/api/v1/topup\": (200, topupResponseData),\n    \"/api/v1/addCard\": (200, addCardResponseData),\n    \"/api/v1/cards\": (200, cardOnFileResponseData)\n  ]\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/AppRoot/SuperAppURLProtocol.swift",
    "content": "import Foundation\n\ntypealias Path = String\ntypealias MockResponse = (statusCode: Int, data: Data?)\n\nfinal class SuperAppURLProtocol: URLProtocol {\n  static var successMock: [Path: MockResponse] = [:]\n  static var failureErrors: [Path: Error] = [:]\n  \n  override class func canInit(with request: URLRequest) -> Bool {\n    return true\n  }\n  \n  override class func canonicalRequest(for request: URLRequest) -> URLRequest {\n    return request\n  }\n  \n  override func startLoading() {\n    if let path = request.url?.path {\n      if let mockResponse = SuperAppURLProtocol.successMock[path] {\n        client?.urlProtocol(self, didReceive: HTTPURLResponse(\n          url: request.url!,\n          statusCode: mockResponse.statusCode,\n          httpVersion: nil,\n          headerFields: nil)!, cacheStoragePolicy: .notAllowed)\n        mockResponse.data.map { client?.urlProtocol(self, didLoad: $0) }\n      } else if let error = SuperAppURLProtocol.failureErrors[path] {\n        client?.urlProtocol(self, didFailWithError: error)\n      } else {\n        client?.urlProtocol(self, didFailWithError: MockSessionError.notSupported)\n      }\n    }\n    \n    client?.urlProtocolDidFinishLoading(self)\n  }\n  \n  override func stopLoading() {\n    \n  }\n\n}\n\nenum MockSessionError: Error {\n  case notSupported\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"19162\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"19144\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Super\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"CX5-z6-Rgm\">\n                                <rect key=\"frame\" x=\"141\" y=\"425.5\" width=\"132.5\" height=\"55\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" weight=\"heavy\" pointSize=\"46\"/>\n                                <color key=\"textColor\" systemColor=\"systemRedColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"CX5-z6-Rgm\" firstAttribute=\"centerX\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerX\" id=\"39B-mV-2d6\"/>\n                            <constraint firstItem=\"CX5-z6-Rgm\" firstAttribute=\"centerY\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerY\" id=\"MVn-2c-RfV\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"52.173913043478265\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <systemColor name=\"systemRedColor\">\n            <color red=\"1\" green=\"0.23137254901960785\" blue=\"0.18823529411764706\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSAllowsLocalNetworking</key>\n\t\t<true/>\n\t</dict>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp/Utils/Array+Utils.swift",
    "content": "import Foundation\n\nextension Array {\n  subscript(safe index: Int) -> Element? {\n    return indices ~= index ? self[index] : nil\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 52;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tF505938C27114F880087571C /* AddPaymentMethod in Frameworks */ = {isa = PBXBuildFile; productRef = F505938B27114F880087571C /* AddPaymentMethod */; };\n\t\tF530665527117188007DAE42 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = F530665427117188007DAE42 /* Network */; };\n\t\tF530665727117188007DAE42 /* NetworkImp in Frameworks */ = {isa = PBXBuildFile; productRef = F530665627117188007DAE42 /* NetworkImp */; };\n\t\tF5306659271171A6007DAE42 /* BaseURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5306658271171A6007DAE42 /* BaseURL.swift */; };\n\t\tF530665B27117244007DAE42 /* SuperAppURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F530665A27117244007DAE42 /* SuperAppURLProtocol.swift */; };\n\t\tF530665D271173B0007DAE42 /* SetupURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F530665C271173B0007DAE42 /* SetupURLProtocol.swift */; };\n\t\tF53FF07D271156E4004F3972 /* FinanceEntity in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF07C271156E4004F3972 /* FinanceEntity */; };\n\t\tF53FF07F271156E5004F3972 /* FinanceRepository in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF07E271156E5004F3972 /* FinanceRepository */; };\n\t\tF53FF081271156E5004F3972 /* CombineUtil in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF080271156E5004F3972 /* CombineUtil */; };\n\t\tF53FF083271156E5004F3972 /* RIBsUtil in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF082271156E5004F3972 /* RIBsUtil */; };\n\t\tF53FF085271156E5004F3972 /* SuperUI in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF084271156E5004F3972 /* SuperUI */; };\n\t\tF53FF087271158C1004F3972 /* Topup in Frameworks */ = {isa = PBXBuildFile; productRef = F53FF086271158C1004F3972 /* Topup */; };\n\t\tF542F83227115B9500085E91 /* FinanceHome in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83127115B9500085E91 /* FinanceHome */; };\n\t\tF542F83627115C3200085E91 /* TransportHome in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83527115C3200085E91 /* TransportHome */; };\n\t\tF542F83827115D4D00085E91 /* AppHome in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83727115D4D00085E91 /* AppHome */; };\n\t\tF542F83A27115ED500085E91 /* ProfileHome in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83927115ED500085E91 /* ProfileHome */; };\n\t\tF542F83C271164F100085E91 /* AppRootComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F542F83B271164F100085E91 /* AppRootComponent.swift */; };\n\t\tF542F83E2711653900085E91 /* TransportHomeImp in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83D2711653900085E91 /* TransportHomeImp */; };\n\t\tF542F840271167EA00085E91 /* TopupImp in Frameworks */ = {isa = PBXBuildFile; productRef = F542F83F271167EA00085E91 /* TopupImp */; };\n\t\tF54E9D9327116A42008D50BF /* AddPaymentMethodImp in Frameworks */ = {isa = PBXBuildFile; productRef = F54E9D9227116A42008D50BF /* AddPaymentMethodImp */; };\n\t\tF55A6E0D27127BE300DD30F7 /* AddPaymentMethod in Frameworks */ = {isa = PBXBuildFile; productRef = F55A6E0C27127BE300DD30F7 /* AddPaymentMethod */; };\n\t\tF55A6E0F27127BE300DD30F7 /* AddPaymentMethodImp in Frameworks */ = {isa = PBXBuildFile; productRef = F55A6E0E27127BE300DD30F7 /* AddPaymentMethodImp */; };\n\t\tF55A6E1127127BE300DD30F7 /* AddPaymentMethodTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = F55A6E1027127BE300DD30F7 /* AddPaymentMethodTestSupport */; };\n\t\tF55A6E1327127C3000DD30F7 /* FinanceRepository in Frameworks */ = {isa = PBXBuildFile; productRef = F55A6E1227127C3000DD30F7 /* FinanceRepository */; };\n\t\tF55A6E1527127C3000DD30F7 /* FinanceRepositoryTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = F55A6E1427127C3000DD30F7 /* FinanceRepositoryTestSupport */; };\n\t\tF57F6AFE26DB4CD700C0117D /* RootTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */; };\n\t\tF5829F5926DB2BC400BFA8CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F5826DB2BC400BFA8CD /* AppDelegate.swift */; };\n\t\tF5829F6226DB2BC500BFA8CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F5829F6126DB2BC500BFA8CD /* Assets.xcassets */; };\n\t\tF5829F6526DB2BC500BFA8CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */; };\n\t\tF5829F6E26DB2DE900BFA8CD /* ModernRIBs in Frameworks */ = {isa = PBXBuildFile; productRef = F5829F6D26DB2DE900BFA8CD /* ModernRIBs */; };\n\t\tF5829F7426DB34AA00BFA8CD /* AppRootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */; };\n\t\tF5829F7626DB34AA00BFA8CD /* AppRootBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */; };\n\t\tF5829F7726DB34AA00BFA8CD /* AppRootInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */; };\n\t\tF5829F7926DB397300BFA8CD /* AppComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5829F7826DB397300BFA8CD /* AppComponent.swift */; };\n\t\tF58BACF2270BF21100E64072 /* CombineExt in Frameworks */ = {isa = PBXBuildFile; productRef = F58BACF1270BF21100E64072 /* CombineExt */; };\n\t\tF58BAD60270C2C9F00E64072 /* Array+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58BAD5F270C2C9F00E64072 /* Array+Utils.swift */; };\n\t\tF5E29F55271271F700A9EF5F /* TopupImpUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E29F54271271F700A9EF5F /* TopupImpUITests.swift */; };\n\t\tF5E29F5A271273DB00A9EF5F /* PlatformTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = F5E29F59271273DB00A9EF5F /* PlatformTestSupport */; };\n\t\tF5E29F5D2712744700A9EF5F /* cardOnFile.json in Resources */ = {isa = PBXBuildFile; fileRef = F5E29F5C2712744700A9EF5F /* cardOnFile.json */; };\n\t\tF5E29F5F271274C100A9EF5F /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E29F5E271274C100A9EF5F /* TestUtil.swift */; };\n\t\tF5E29F612712751B00A9EF5F /* topupSuccessResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = F5E29F602712751B00A9EF5F /* topupSuccessResponse.json */; };\n\t\tF5E29FAB27127A9200A9EF5F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E29FAA27127A9200A9EF5F /* AppDelegate.swift */; };\n\t\tF5E29FB427127A9400A9EF5F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F5E29FB327127A9400A9EF5F /* Assets.xcassets */; };\n\t\tF5E29FB727127A9400A9EF5F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F5E29FB527127A9400A9EF5F /* LaunchScreen.storyboard */; };\n\t\tF5E29FC427127AE700A9EF5F /* AddPaymentMethodIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E29FC327127AE700A9EF5F /* AddPaymentMethodIntegrationTests.swift */; };\n\t\tF5E29FCC27127B2800A9EF5F /* PlatformTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = F5E29FCB27127B2800A9EF5F /* PlatformTestSupport */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tF58CE267271271310033575A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F5829F4D26DB2BC400BFA8CD /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F5829F5426DB2BC400BFA8CD;\n\t\t\tremoteInfo = MiniSuperApp;\n\t\t};\n\t\tF5E29FC527127AE700A9EF5F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F5829F4D26DB2BC400BFA8CD /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F5E29FA727127A9100A9EF5F;\n\t\t\tremoteInfo = TestHost;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\tF505938527114E3C0087571C /* CX */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CX; sourceTree = \"<group>\"; };\n\t\tF505938627114E4A0087571C /* Finance */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Finance; sourceTree = \"<group>\"; };\n\t\tF505938727114E560087571C /* Transport */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Transport; sourceTree = \"<group>\"; };\n\t\tF505938827114E650087571C /* Profile */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Profile; sourceTree = \"<group>\"; };\n\t\tF505938927114E8F0087571C /* Platform */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Platform; sourceTree = \"<group>\"; };\n\t\tF5306658271171A6007DAE42 /* BaseURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseURL.swift; sourceTree = \"<group>\"; };\n\t\tF530665A27117244007DAE42 /* SuperAppURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperAppURLProtocol.swift; sourceTree = \"<group>\"; };\n\t\tF530665C271173B0007DAE42 /* SetupURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupURLProtocol.swift; sourceTree = \"<group>\"; };\n\t\tF542F83B271164F100085E91 /* AppRootComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootComponent.swift; sourceTree = \"<group>\"; };\n\t\tF57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootTabBarController.swift; sourceTree = \"<group>\"; };\n\t\tF5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MiniSuperApp.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF5829F5826DB2BC400BFA8CD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF5829F6126DB2BC500BFA8CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF5829F6426DB2BC500BFA8CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tF5829F6626DB2BC500BFA8CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tF5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootRouter.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootBuilder.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootInteractor.swift; sourceTree = \"<group>\"; };\n\t\tF5829F7826DB397300BFA8CD /* AppComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppComponent.swift; sourceTree = \"<group>\"; };\n\t\tF58BAD5F270C2C9F00E64072 /* Array+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Array+Utils.swift\"; sourceTree = \"<group>\"; };\n\t\tF58CE261271271310033575A /* MiniSuperAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MiniSuperAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF5E29F54271271F700A9EF5F /* TopupImpUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopupImpUITests.swift; sourceTree = \"<group>\"; };\n\t\tF5E29F5C2712744700A9EF5F /* cardOnFile.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = cardOnFile.json; sourceTree = \"<group>\"; };\n\t\tF5E29F5E271274C100A9EF5F /* TestUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = \"<group>\"; };\n\t\tF5E29F602712751B00A9EF5F /* topupSuccessResponse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = topupSuccessResponse.json; sourceTree = \"<group>\"; };\n\t\tF5E29FA827127A9100A9EF5F /* TestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHost.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF5E29FAA27127A9200A9EF5F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF5E29FB327127A9400A9EF5F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF5E29FB627127A9400A9EF5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tF5E29FB827127A9400A9EF5F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tF5E29FC127127AE700A9EF5F /* AddPaymentMethodIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AddPaymentMethodIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF5E29FC327127AE700A9EF5F /* AddPaymentMethodIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPaymentMethodIntegrationTests.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tF5829F5226DB2BC400BFA8CD /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF58BACF2270BF21100E64072 /* CombineExt in Frameworks */,\n\t\t\t\tF54E9D9327116A42008D50BF /* AddPaymentMethodImp in Frameworks */,\n\t\t\t\tF505938C27114F880087571C /* AddPaymentMethod in Frameworks */,\n\t\t\t\tF53FF081271156E5004F3972 /* CombineUtil in Frameworks */,\n\t\t\t\tF530665727117188007DAE42 /* NetworkImp in Frameworks */,\n\t\t\t\tF542F83A27115ED500085E91 /* ProfileHome in Frameworks */,\n\t\t\t\tF542F83E2711653900085E91 /* TransportHomeImp in Frameworks */,\n\t\t\t\tF542F840271167EA00085E91 /* TopupImp in Frameworks */,\n\t\t\t\tF53FF087271158C1004F3972 /* Topup in Frameworks */,\n\t\t\t\tF53FF07D271156E4004F3972 /* FinanceEntity in Frameworks */,\n\t\t\t\tF53FF085271156E5004F3972 /* SuperUI in Frameworks */,\n\t\t\t\tF542F83627115C3200085E91 /* TransportHome in Frameworks */,\n\t\t\t\tF5829F6E26DB2DE900BFA8CD /* ModernRIBs in Frameworks */,\n\t\t\t\tF53FF083271156E5004F3972 /* RIBsUtil in Frameworks */,\n\t\t\t\tF530665527117188007DAE42 /* Network in Frameworks */,\n\t\t\t\tF542F83827115D4D00085E91 /* AppHome in Frameworks */,\n\t\t\t\tF542F83227115B9500085E91 /* FinanceHome in Frameworks */,\n\t\t\t\tF53FF07F271156E5004F3972 /* FinanceRepository in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF58CE25E271271310033575A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29F5A271273DB00A9EF5F /* PlatformTestSupport in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FA527127A9100A9EF5F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FBE27127AE700A9EF5F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF55A6E1127127BE300DD30F7 /* AddPaymentMethodTestSupport in Frameworks */,\n\t\t\t\tF55A6E1527127C3000DD30F7 /* FinanceRepositoryTestSupport in Frameworks */,\n\t\t\t\tF55A6E0F27127BE300DD30F7 /* AddPaymentMethodImp in Frameworks */,\n\t\t\t\tF55A6E0D27127BE300DD30F7 /* AddPaymentMethod in Frameworks */,\n\t\t\t\tF5E29FCC27127B2800A9EF5F /* PlatformTestSupport in Frameworks */,\n\t\t\t\tF55A6E1327127C3000DD30F7 /* FinanceRepository in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tF505938A27114F880087571C /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF57F6AFF26DB585100C0117D /* Utils */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF58BAD5F270C2C9F00E64072 /* Array+Utils.swift */,\n\t\t\t);\n\t\t\tpath = Utils;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F4C26DB2BC400BFA8CD = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF505938927114E8F0087571C /* Platform */,\n\t\t\t\tF505938827114E650087571C /* Profile */,\n\t\t\t\tF505938727114E560087571C /* Transport */,\n\t\t\t\tF505938627114E4A0087571C /* Finance */,\n\t\t\t\tF505938527114E3C0087571C /* CX */,\n\t\t\t\tF5829F5726DB2BC400BFA8CD /* MiniSuperApp */,\n\t\t\t\tF58CE262271271310033575A /* MiniSuperAppUITests */,\n\t\t\t\tF5E29FA927127A9100A9EF5F /* TestHost */,\n\t\t\t\tF5E29FC227127AE700A9EF5F /* AddPaymentMethodIntegrationTests */,\n\t\t\t\tF5829F5626DB2BC400BFA8CD /* Products */,\n\t\t\t\tF505938A27114F880087571C /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F5626DB2BC400BFA8CD /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */,\n\t\t\t\tF58CE261271271310033575A /* MiniSuperAppUITests.xctest */,\n\t\t\t\tF5E29FA827127A9100A9EF5F /* TestHost.app */,\n\t\t\t\tF5E29FC127127AE700A9EF5F /* AddPaymentMethodIntegrationTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F5726DB2BC400BFA8CD /* MiniSuperApp */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F7A26DB3AF900BFA8CD /* AppDelegate */,\n\t\t\t\tF5829F6F26DB33CF00BFA8CD /* AppRoot */,\n\t\t\t\tF57F6AFF26DB585100C0117D /* Utils */,\n\t\t\t\tF5829F6126DB2BC500BFA8CD /* Assets.xcassets */,\n\t\t\t\tF5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */,\n\t\t\t\tF5829F6626DB2BC500BFA8CD /* Info.plist */,\n\t\t\t);\n\t\t\tpath = MiniSuperApp;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F6F26DB33CF00BFA8CD /* AppRoot */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F7026DB34AA00BFA8CD /* AppRootRouter.swift */,\n\t\t\t\tF5829F7226DB34AA00BFA8CD /* AppRootBuilder.swift */,\n\t\t\t\tF5829F7326DB34AA00BFA8CD /* AppRootInteractor.swift */,\n\t\t\t\tF57F6AFD26DB4CD700C0117D /* RootTabBarController.swift */,\n\t\t\t\tF542F83B271164F100085E91 /* AppRootComponent.swift */,\n\t\t\t\tF5306658271171A6007DAE42 /* BaseURL.swift */,\n\t\t\t\tF530665A27117244007DAE42 /* SuperAppURLProtocol.swift */,\n\t\t\t\tF530665C271173B0007DAE42 /* SetupURLProtocol.swift */,\n\t\t\t);\n\t\t\tpath = AppRoot;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5829F7A26DB3AF900BFA8CD /* AppDelegate */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F5826DB2BC400BFA8CD /* AppDelegate.swift */,\n\t\t\t\tF5829F7826DB397300BFA8CD /* AppComponent.swift */,\n\t\t\t);\n\t\t\tpath = AppDelegate;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF58CE262271271310033575A /* MiniSuperAppUITests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5E29F5B2712743300A9EF5F /* Response */,\n\t\t\t\tF5E29F54271271F700A9EF5F /* TopupImpUITests.swift */,\n\t\t\t\tF5E29F5E271274C100A9EF5F /* TestUtil.swift */,\n\t\t\t);\n\t\t\tpath = MiniSuperAppUITests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5E29F5B2712743300A9EF5F /* Response */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5E29F5C2712744700A9EF5F /* cardOnFile.json */,\n\t\t\t\tF5E29F602712751B00A9EF5F /* topupSuccessResponse.json */,\n\t\t\t);\n\t\t\tpath = Response;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5E29FA927127A9100A9EF5F /* TestHost */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5E29FAA27127A9200A9EF5F /* AppDelegate.swift */,\n\t\t\t\tF5E29FB327127A9400A9EF5F /* Assets.xcassets */,\n\t\t\t\tF5E29FB527127A9400A9EF5F /* LaunchScreen.storyboard */,\n\t\t\t\tF5E29FB827127A9400A9EF5F /* Info.plist */,\n\t\t\t);\n\t\t\tpath = TestHost;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5E29FC227127AE700A9EF5F /* AddPaymentMethodIntegrationTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF5E29FC327127AE700A9EF5F /* AddPaymentMethodIntegrationTests.swift */,\n\t\t\t);\n\t\t\tpath = AddPaymentMethodIntegrationTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tF5829F5426DB2BC400BFA8CD /* MiniSuperApp */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F5829F6926DB2BC500BFA8CD /* Build configuration list for PBXNativeTarget \"MiniSuperApp\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF5829F5126DB2BC400BFA8CD /* Sources */,\n\t\t\t\tF5829F5226DB2BC400BFA8CD /* Frameworks */,\n\t\t\t\tF5829F5326DB2BC400BFA8CD /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MiniSuperApp;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF5829F6D26DB2DE900BFA8CD /* ModernRIBs */,\n\t\t\t\tF58BACF1270BF21100E64072 /* CombineExt */,\n\t\t\t\tF505938B27114F880087571C /* AddPaymentMethod */,\n\t\t\t\tF53FF07C271156E4004F3972 /* FinanceEntity */,\n\t\t\t\tF53FF07E271156E5004F3972 /* FinanceRepository */,\n\t\t\t\tF53FF080271156E5004F3972 /* CombineUtil */,\n\t\t\t\tF53FF082271156E5004F3972 /* RIBsUtil */,\n\t\t\t\tF53FF084271156E5004F3972 /* SuperUI */,\n\t\t\t\tF53FF086271158C1004F3972 /* Topup */,\n\t\t\t\tF542F83127115B9500085E91 /* FinanceHome */,\n\t\t\t\tF542F83527115C3200085E91 /* TransportHome */,\n\t\t\t\tF542F83727115D4D00085E91 /* AppHome */,\n\t\t\t\tF542F83927115ED500085E91 /* ProfileHome */,\n\t\t\t\tF542F83D2711653900085E91 /* TransportHomeImp */,\n\t\t\t\tF542F83F271167EA00085E91 /* TopupImp */,\n\t\t\t\tF54E9D9227116A42008D50BF /* AddPaymentMethodImp */,\n\t\t\t\tF530665427117188007DAE42 /* Network */,\n\t\t\t\tF530665627117188007DAE42 /* NetworkImp */,\n\t\t\t);\n\t\t\tproductName = SuperRedApp;\n\t\t\tproductReference = F5829F5526DB2BC400BFA8CD /* MiniSuperApp.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tF58CE260271271310033575A /* MiniSuperAppUITests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F58CE26B271271310033575A /* Build configuration list for PBXNativeTarget \"MiniSuperAppUITests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF58CE25D271271310033575A /* Sources */,\n\t\t\t\tF58CE25E271271310033575A /* Frameworks */,\n\t\t\t\tF58CE25F271271310033575A /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF58CE268271271310033575A /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MiniSuperAppUITests;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF5E29F59271273DB00A9EF5F /* PlatformTestSupport */,\n\t\t\t);\n\t\t\tproductName = MiniSuperAppUITests;\n\t\t\tproductReference = F58CE261271271310033575A /* MiniSuperAppUITests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.ui-testing\";\n\t\t};\n\t\tF5E29FA727127A9100A9EF5F /* TestHost */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F5E29FB927127A9400A9EF5F /* Build configuration list for PBXNativeTarget \"TestHost\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF5E29FA427127A9100A9EF5F /* Sources */,\n\t\t\t\tF5E29FA527127A9100A9EF5F /* Frameworks */,\n\t\t\t\tF5E29FA627127A9100A9EF5F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = TestHost;\n\t\t\tproductName = TestHost;\n\t\t\tproductReference = F5E29FA827127A9100A9EF5F /* TestHost.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tF5E29FC027127AE700A9EF5F /* AddPaymentMethodIntegrationTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F5E29FC727127AE700A9EF5F /* Build configuration list for PBXNativeTarget \"AddPaymentMethodIntegrationTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF5E29FBD27127AE700A9EF5F /* Sources */,\n\t\t\t\tF5E29FBE27127AE700A9EF5F /* Frameworks */,\n\t\t\t\tF5E29FBF27127AE700A9EF5F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF5E29FC627127AE700A9EF5F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = AddPaymentMethodIntegrationTests;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF5E29FCB27127B2800A9EF5F /* PlatformTestSupport */,\n\t\t\t\tF55A6E0C27127BE300DD30F7 /* AddPaymentMethod */,\n\t\t\t\tF55A6E0E27127BE300DD30F7 /* AddPaymentMethodImp */,\n\t\t\t\tF55A6E1027127BE300DD30F7 /* AddPaymentMethodTestSupport */,\n\t\t\t\tF55A6E1227127C3000DD30F7 /* FinanceRepository */,\n\t\t\t\tF55A6E1427127C3000DD30F7 /* FinanceRepositoryTestSupport */,\n\t\t\t);\n\t\t\tproductName = AddPaymentMethodIntegrationTests;\n\t\t\tproductReference = F5E29FC127127AE700A9EF5F /* AddPaymentMethodIntegrationTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tF5829F4D26DB2BC400BFA8CD /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1300;\n\t\t\t\tLastUpgradeCheck = 1250;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tF5829F5426DB2BC400BFA8CD = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.5.1;\n\t\t\t\t\t};\n\t\t\t\t\tF58CE260271271310033575A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.0;\n\t\t\t\t\t\tTestTargetID = F5829F5426DB2BC400BFA8CD;\n\t\t\t\t\t};\n\t\t\t\t\tF5E29FA727127A9100A9EF5F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.0;\n\t\t\t\t\t};\n\t\t\t\t\tF5E29FC027127AE700A9EF5F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.0;\n\t\t\t\t\t\tTestTargetID = F5E29FA727127A9100A9EF5F;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = F5829F5026DB2BC400BFA8CD /* Build configuration list for PBXProject \"MiniSuperApp\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = F5829F4C26DB2BC400BFA8CD;\n\t\t\tpackageReferences = (\n\t\t\t\tF5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */,\n\t\t\t\tF58BACF0270BF21100E64072 /* XCRemoteSwiftPackageReference \"CombineExt\" */,\n\t\t\t);\n\t\t\tproductRefGroup = F5829F5626DB2BC400BFA8CD /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tF5829F5426DB2BC400BFA8CD /* MiniSuperApp */,\n\t\t\t\tF58CE260271271310033575A /* MiniSuperAppUITests */,\n\t\t\t\tF5E29FA727127A9100A9EF5F /* TestHost */,\n\t\t\t\tF5E29FC027127AE700A9EF5F /* AddPaymentMethodIntegrationTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tF5829F5326DB2BC400BFA8CD /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5829F6526DB2BC500BFA8CD /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tF5829F6226DB2BC500BFA8CD /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF58CE25F271271310033575A /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29F5D2712744700A9EF5F /* cardOnFile.json in Resources */,\n\t\t\t\tF5E29F612712751B00A9EF5F /* topupSuccessResponse.json in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FA627127A9100A9EF5F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29FB727127A9400A9EF5F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tF5E29FB427127A9400A9EF5F /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FBF27127AE700A9EF5F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tF5829F5126DB2BC400BFA8CD /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5829F7626DB34AA00BFA8CD /* AppRootBuilder.swift in Sources */,\n\t\t\t\tF530665D271173B0007DAE42 /* SetupURLProtocol.swift in Sources */,\n\t\t\t\tF530665B27117244007DAE42 /* SuperAppURLProtocol.swift in Sources */,\n\t\t\t\tF542F83C271164F100085E91 /* AppRootComponent.swift in Sources */,\n\t\t\t\tF5829F7726DB34AA00BFA8CD /* AppRootInteractor.swift in Sources */,\n\t\t\t\tF57F6AFE26DB4CD700C0117D /* RootTabBarController.swift in Sources */,\n\t\t\t\tF5829F5926DB2BC400BFA8CD /* AppDelegate.swift in Sources */,\n\t\t\t\tF5829F7926DB397300BFA8CD /* AppComponent.swift in Sources */,\n\t\t\t\tF5829F7426DB34AA00BFA8CD /* AppRootRouter.swift in Sources */,\n\t\t\t\tF58BAD60270C2C9F00E64072 /* Array+Utils.swift in Sources */,\n\t\t\t\tF5306659271171A6007DAE42 /* BaseURL.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF58CE25D271271310033575A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29F5F271274C100A9EF5F /* TestUtil.swift in Sources */,\n\t\t\t\tF5E29F55271271F700A9EF5F /* TopupImpUITests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FA427127A9100A9EF5F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29FAB27127A9200A9EF5F /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5E29FBD27127AE700A9EF5F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5E29FC427127AE700A9EF5F /* AddPaymentMethodIntegrationTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tF58CE268271271310033575A /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F5829F5426DB2BC400BFA8CD /* MiniSuperApp */;\n\t\t\ttargetProxy = F58CE267271271310033575A /* PBXContainerItemProxy */;\n\t\t};\n\t\tF5E29FC627127AE700A9EF5F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F5E29FA727127A9100A9EF5F /* TestHost */;\n\t\t\ttargetProxy = F5E29FC527127AE700A9EF5F /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tF5829F6326DB2BC500BFA8CD /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tF5829F6426DB2BC500BFA8CD /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF5E29FB527127A9400A9EF5F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tF5E29FB627127A9400A9EF5F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tF5829F6726DB2BC500BFA8CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5829F6826DB2BC500BFA8CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF5829F6A26DB2BC500BFA8CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = MiniSuperApp/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5829F6B26DB2BC500BFA8CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = MiniSuperApp/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF58CE269271271310033575A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperAppUITests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_TARGET_NAME = MiniSuperApp;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF58CE26A271271310033575A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperAppUITests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_TARGET_NAME = MiniSuperApp;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF5E29F56271272CC00A9EF5F /* UITESTING */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 14.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = UITESTING;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = UITESTING;\n\t\t};\n\t\tF5E29F57271272CC00A9EF5F /* UITESTING */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = MiniSuperApp/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperApp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = UITESTING;\n\t\t};\n\t\tF5E29F58271272CC00A9EF5F /* UITESTING */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.MiniSuperAppUITests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_TARGET_NAME = MiniSuperApp;\n\t\t\t};\n\t\t\tname = UITESTING;\n\t\t};\n\t\tF5E29FBA27127A9400A9EF5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = TestHost/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.TestHost;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5E29FBB27127A9400A9EF5F /* UITESTING */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = TestHost/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.TestHost;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = UITESTING;\n\t\t};\n\t\tF5E29FBC27127A9400A9EF5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = TestHost/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.TestHost;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF5E29FC827127AE700A9EF5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.AddPaymentMethodIntegrationTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF5E29FC927127AE700A9EF5F /* UITESTING */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.AddPaymentMethodIntegrationTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost\";\n\t\t\t};\n\t\t\tname = UITESTING;\n\t\t};\n\t\tF5E29FCA27127AE700A9EF5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.red.AddPaymentMethodIntegrationTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tF5829F5026DB2BC400BFA8CD /* Build configuration list for PBXProject \"MiniSuperApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5829F6726DB2BC500BFA8CD /* Debug */,\n\t\t\t\tF5E29F56271272CC00A9EF5F /* UITESTING */,\n\t\t\t\tF5829F6826DB2BC500BFA8CD /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF5829F6926DB2BC500BFA8CD /* Build configuration list for PBXNativeTarget \"MiniSuperApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5829F6A26DB2BC500BFA8CD /* Debug */,\n\t\t\t\tF5E29F57271272CC00A9EF5F /* UITESTING */,\n\t\t\t\tF5829F6B26DB2BC500BFA8CD /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF58CE26B271271310033575A /* Build configuration list for PBXNativeTarget \"MiniSuperAppUITests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF58CE269271271310033575A /* Debug */,\n\t\t\t\tF5E29F58271272CC00A9EF5F /* UITESTING */,\n\t\t\t\tF58CE26A271271310033575A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF5E29FB927127A9400A9EF5F /* Build configuration list for PBXNativeTarget \"TestHost\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5E29FBA27127A9400A9EF5F /* Debug */,\n\t\t\t\tF5E29FBB27127A9400A9EF5F /* UITESTING */,\n\t\t\t\tF5E29FBC27127A9400A9EF5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF5E29FC727127AE700A9EF5F /* Build configuration list for PBXNativeTarget \"AddPaymentMethodIntegrationTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF5E29FC827127AE700A9EF5F /* Debug */,\n\t\t\t\tF5E29FC927127AE700A9EF5F /* UITESTING */,\n\t\t\t\tF5E29FCA27127AE700A9EF5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\tF5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/DevYeom/ModernRIBs\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.0.1;\n\t\t\t};\n\t\t};\n\t\tF58BACF0270BF21100E64072 /* XCRemoteSwiftPackageReference \"CombineExt\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/CombineCommunity/CombineExt\";\n\t\t\trequirement = {\n\t\t\t\tkind = exactVersion;\n\t\t\t\tversion = 1.5.1;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\tF505938B27114F880087571C /* AddPaymentMethod */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AddPaymentMethod;\n\t\t};\n\t\tF530665427117188007DAE42 /* Network */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Network;\n\t\t};\n\t\tF530665627117188007DAE42 /* NetworkImp */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = NetworkImp;\n\t\t};\n\t\tF53FF07C271156E4004F3972 /* FinanceEntity */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = FinanceEntity;\n\t\t};\n\t\tF53FF07E271156E5004F3972 /* FinanceRepository */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = FinanceRepository;\n\t\t};\n\t\tF53FF080271156E5004F3972 /* CombineUtil */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = CombineUtil;\n\t\t};\n\t\tF53FF082271156E5004F3972 /* RIBsUtil */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = RIBsUtil;\n\t\t};\n\t\tF53FF084271156E5004F3972 /* SuperUI */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = SuperUI;\n\t\t};\n\t\tF53FF086271158C1004F3972 /* Topup */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Topup;\n\t\t};\n\t\tF542F83127115B9500085E91 /* FinanceHome */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = FinanceHome;\n\t\t};\n\t\tF542F83527115C3200085E91 /* TransportHome */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = TransportHome;\n\t\t};\n\t\tF542F83727115D4D00085E91 /* AppHome */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AppHome;\n\t\t};\n\t\tF542F83927115ED500085E91 /* ProfileHome */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = ProfileHome;\n\t\t};\n\t\tF542F83D2711653900085E91 /* TransportHomeImp */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = TransportHomeImp;\n\t\t};\n\t\tF542F83F271167EA00085E91 /* TopupImp */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = TopupImp;\n\t\t};\n\t\tF54E9D9227116A42008D50BF /* AddPaymentMethodImp */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AddPaymentMethodImp;\n\t\t};\n\t\tF55A6E0C27127BE300DD30F7 /* AddPaymentMethod */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AddPaymentMethod;\n\t\t};\n\t\tF55A6E0E27127BE300DD30F7 /* AddPaymentMethodImp */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AddPaymentMethodImp;\n\t\t};\n\t\tF55A6E1027127BE300DD30F7 /* AddPaymentMethodTestSupport */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = AddPaymentMethodTestSupport;\n\t\t};\n\t\tF55A6E1227127C3000DD30F7 /* FinanceRepository */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = FinanceRepository;\n\t\t};\n\t\tF55A6E1427127C3000DD30F7 /* FinanceRepositoryTestSupport */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = FinanceRepositoryTestSupport;\n\t\t};\n\t\tF5829F6D26DB2DE900BFA8CD /* ModernRIBs */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F5829F6C26DB2DE900BFA8CD /* XCRemoteSwiftPackageReference \"ModernRIBs\" */;\n\t\t\tproductName = ModernRIBs;\n\t\t};\n\t\tF58BACF1270BF21100E64072 /* CombineExt */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F58BACF0270BF21100E64072 /* XCRemoteSwiftPackageReference \"CombineExt\" */;\n\t\t\tproductName = CombineExt;\n\t\t};\n\t\tF5E29F59271273DB00A9EF5F /* PlatformTestSupport */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = PlatformTestSupport;\n\t\t};\n\t\tF5E29FCB27127B2800A9EF5F /* PlatformTestSupport */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = PlatformTestSupport;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = F5829F4D26DB2BC400BFA8CD /* Project object */;\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"combine-schedulers\",\n        \"repositoryURL\": \"https://github.com/pointfreeco/combine-schedulers\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"4cf088c29a20f52be0f2ca54992b492c54e0076b\",\n          \"version\": \"0.5.3\"\n        }\n      },\n      {\n        \"package\": \"CombineExt\",\n        \"repositoryURL\": \"https://github.com/CombineCommunity/CombineExt\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"0880829102152185190064fd17847a7c681d2127\",\n          \"version\": \"1.5.1\"\n        }\n      },\n      {\n        \"package\": \"Hammer\",\n        \"repositoryURL\": \"https://github.com/lyft/Hammer\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"1042e3f7b5d7ee30a727a3cd9e90945a1f6daed5\",\n          \"version\": \"0.13.0\"\n        }\n      },\n      {\n        \"package\": \"ModernRIBs\",\n        \"repositoryURL\": \"https://github.com/DevYeom/ModernRIBs\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"5e0a67365a1fb18ca06b919dbf53608843ddc284\",\n          \"version\": \"1.0.1\"\n        }\n      },\n      {\n        \"package\": \"SnapshotTesting\",\n        \"repositoryURL\": \"https://github.com/pointfreeco/swift-snapshot-testing\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"f8a9c997c3c1dab4e216a8ec9014e23144cbab37\",\n          \"version\": \"1.9.0\"\n        }\n      },\n      {\n        \"package\": \"Swifter\",\n        \"repositoryURL\": \"https://github.com/httpswift/swifter\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"9483a5d459b45c3ffd059f7b55f9638e268632fd\",\n          \"version\": \"1.5.0\"\n        }\n      },\n      {\n        \"package\": \"xctest-dynamic-overlay\",\n        \"repositoryURL\": \"https://github.com/pointfreeco/xctest-dynamic-overlay\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"50a70a9d3583fe228ce672e8923010c8df2deddd\",\n          \"version\": \"0.2.1\"\n        }\n      }\n    ]\n  },\n  \"version\": 1\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/xcshareddata/xcschemes/AddPaymentMethodIntegrationTests.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5E29FC027127AE700A9EF5F\"\n               BuildableName = \"AddPaymentMethodIntegrationTests.xctest\"\n               BlueprintName = \"AddPaymentMethodIntegrationTests\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/xcshareddata/xcschemes/MiniSuperApp.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1250\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n               BuildableName = \"MiniSuperApp.app\"\n               BlueprintName = \"MiniSuperApp\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"TopupImpTests\"\n               BuildableName = \"TopupImpTests\"\n               BlueprintName = \"TopupImpTests\"\n               ReferencedContainer = \"container:Finance\">\n            </BuildableReference>\n         </TestableReference>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F58CE260271271310033575A\"\n               BuildableName = \"MiniSuperAppUITests.xctest\"\n               BlueprintName = \"MiniSuperAppUITests\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n            BuildableName = \"MiniSuperApp.app\"\n            BlueprintName = \"MiniSuperApp\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5829F5426DB2BC400BFA8CD\"\n            BuildableName = \"MiniSuperApp.app\"\n            BlueprintName = \"MiniSuperApp\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/xcshareddata/xcschemes/MiniSuperAppUITests.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"UITESTING\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F58CE260271271310033575A\"\n               BuildableName = \"MiniSuperAppUITests.xctest\"\n               BlueprintName = \"MiniSuperAppUITests\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperApp.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5E29FA727127A9100A9EF5F\"\n               BuildableName = \"TestHost.app\"\n               BlueprintName = \"TestHost\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5E29F7E271277F900A9EF5F\"\n               BuildableName = \"TopupImpIntegrationTests.xctest\"\n               BlueprintName = \"TopupImpIntegrationTests\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F5E29FC027127AE700A9EF5F\"\n               BuildableName = \"AddPaymentMethodIntegrationTests.xctest\"\n               BlueprintName = \"AddPaymentMethodIntegrationTests\"\n               ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5E29FA727127A9100A9EF5F\"\n            BuildableName = \"TestHost.app\"\n            BlueprintName = \"TestHost\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F5E29FA727127A9100A9EF5F\"\n            BuildableName = \"TestHost.app\"\n            BlueprintName = \"TestHost\"\n            ReferencedContainer = \"container:MiniSuperApp.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperAppUITests/Response/cardOnFile.json",
    "content": "{\n  \"cards\": [\n    {\n      \"id\": \"0\",\n      \"name\": \"우리은행\",\n      \"digits\": \"0123\",\n      \"color\": \"#f19a38ff\",\n      \"isPrimary\": false\n    },\n    {\n      \"id\": \"1\",\n      \"name\": \"신한카드\",\n      \"digits\": \"0987\",\n      \"color\": \"#3478f6ff\",\n      \"isPrimary\": false\n    },\n    {\n      \"id\": \"2\",\n      \"name\": \"현대카드\",\n      \"digits\": \"8765\",\n      \"color\": \"#78c5f5ff\",\n      \"isPrimary\": false\n    }\n  ]\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperAppUITests/Response/topupSuccessResponse.json",
    "content": "{\n  \"status\": \"success\"\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperAppUITests/TestUtil.swift",
    "content": "import Foundation\n\nenum TestUtilError: Error {\n  case fileNotFound\n}\n\nfinal class TestUtil {\n  static func path(for fileName: String, in bundleClass: AnyClass) throws -> String {\n    if let path = Bundle(for: bundleClass).path(forResource: fileName, ofType: nil) {\n      return path\n    } else {\n      throw TestUtilError.fileNotFound\n    }\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/MiniSuperAppUITests/TopupImpUITests.swift",
    "content": "import XCTest\nimport Swifter\n\nfinal class TopupImpUITests: XCTestCase {\n  \n  private var app: XCUIApplication!\n  private var server: HttpServer!\n  \n  override func setUpWithError() throws {\n    continueAfterFailure = false\n    \n    server = HttpServer()\n    \n    app = XCUIApplication()\n  }\n  \n  func testTopupSuccess() throws {\n    // given\n    let cardOnFileJSONPath = try TestUtil.path(for: \"cardOnFile.json\", in: type(of: self))\n    server[\"/cards\"] = shareFile(cardOnFileJSONPath)\n    \n    let topupResponse = try TestUtil.path(for: \"topupSuccessResponse.json\", in: type(of: self))\n    server[\"/topup\"] = shareFile(topupResponse)\n    \n    // when\n    try server.start()\n    app.launch()\n    \n    // then\n    app.tabBars.buttons[\"superpay_home_tab_bar_item\"].tap()\n    app.buttons[\"superpay_dashboard_topup_button\"].tap()\n    \n    let textField = app.textFields[\"topup_enteramount_textfield\"]\n    textField.tap()\n    textField.typeText(\"10000\")\n    \n    app.buttons[\"topup_enteramount_confirm_button\"].tap()\n    \n    XCTAssertEqual(app.staticTexts.element(matching: .any, identifier: \"superpay_dashboard_balance_label\").label, \"10,000\")\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"Platform\",\n  platforms: [.iOS(.v14)],\n  products: [\n    .library(\n      name: \"CombineUtil\",\n      targets: [\"CombineUtil\"]\n    ),\n    .library(\n      name: \"RIBsUtil\",\n      targets: [\"RIBsUtil\"]\n    ),\n    .library(\n      name: \"RIBsTestSupport\",\n      targets: [\"RIBsTestSupport\"]\n    ),\n    .library(\n      name: \"PlatformTestSupport\",\n      targets: [\"PlatformTestSupport\"]\n    ),\n    .library(\n      name: \"SuperUI\",\n      targets: [\"SuperUI\"]\n    ),\n    .library(\n      name: \"DefaultsStore\",\n      targets: [\"DefaultsStore\"]\n    ),\n    .library(\n      name: \"Network\",\n      targets: [\"Network\"]\n    ),\n    .library(\n      name: \"NetworkImp\",\n      targets: [\"NetworkImp\"]\n    ),\n  ],\n  dependencies: [\n    .package(url: \"https://github.com/CombineCommunity/CombineExt\", from: \"1.0.0\"),\n    .package(name: \"ModernRIBs\", url: \"https://github.com/DevYeom/ModernRIBs\", .exact(\"1.0.1\")),\n    .package(url: \"https://github.com/pointfreeco/combine-schedulers\", from: \"0.5.3\"),\n    .package(name: \"SnapshotTesting\", url: \"https://github.com/pointfreeco/swift-snapshot-testing\", from: \"1.9.0\"),\n    .package(name: \"Swifter\", url: \"https://github.com/httpswift/swifter\", from: \"1.5.0\"),\n    .package(name: \"Hammer\", url: \"https://github.com/lyft/Hammer\", .exact(\"0.13.0\")),\n  ],\n  targets: [\n    .target(\n      name: \"CombineUtil\",\n      dependencies: [\n        \"CombineExt\",\n        .product(name: \"CombineSchedulers\", package: \"combine-schedulers\")\n      ]\n    ),\n    .target(\n      name: \"RIBsUtil\",\n      dependencies: [\n        \"ModernRIBs\"\n      ]\n    ),\n    .target(\n      name: \"RIBsTestSupport\",\n      dependencies: [\n        \"ModernRIBs\"\n      ]\n    ),\n    .target(\n      name: \"PlatformTestSupport\",\n      dependencies: [\n        \"SnapshotTesting\",\n        \"Swifter\",\n        \"Hammer\"\n      ]\n    ),\n    .target(\n      name: \"SuperUI\",\n      dependencies: [\n        \"RIBsUtil\"\n      ]\n    ),\n    .target(\n      name: \"DefaultsStore\",\n      dependencies: [\n      ]\n    ),\n    .target(\n      name: \"Network\",\n      dependencies: [\n      ]\n    ),\n    .target(\n      name: \"NetworkImp\",\n      dependencies: [\n        \"Network\"\n      ]\n    ),\n  ]\n)\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/README.md",
    "content": "# Platform\n\nA description of this package.\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/CombineUtil/Combine+Utils.swift",
    "content": "import Combine\nimport CombineExt\nimport Foundation\n\npublic class ReadOnlyCurrentValuePublisher<Element>: Publisher {\n  \n  public typealias Output = Element\n  public typealias Failure = Never\n  \n  public var value: Element {\n    currentValueRelay.value\n  }\n  \n  fileprivate let currentValueRelay: CurrentValueRelay<Output>\n  \n  fileprivate init(_ initialValue: Element) {\n    currentValueRelay = CurrentValueRelay(initialValue)\n  }\n  \n  public func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Element == S.Input {\n    currentValueRelay.receive(subscriber: subscriber)\n  }\n  \n}\n\npublic final class CurrentValuePublisher<Element>: ReadOnlyCurrentValuePublisher<Element> {\n  \n  typealias Output = Element\n  typealias Failure = Never\n  \n  public override init(_ initialValue: Element) {\n    super.init(initialValue)\n  }\n\n  public func send(_ value: Element) {\n    currentValueRelay.accept(value)\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/DefaultsStore/DefaultsStore.swift",
    "content": "import Foundation\n\npublic protocol DefaultsStore {\n  var isInitialLaunch: Bool { get set }\n  var lastNoticeDate: Double { get set }\n}\n\npublic struct DefaultsStoreImp: DefaultsStore {\n  \n  public var isInitialLaunch: Bool {\n    get {\n      userDefaults.bool(forKey: kIsInitialLaunch)\n    }\n    set {\n      userDefaults.set(newValue, forKey: kIsInitialLaunch)\n    }\n  }\n  \n  public var lastNoticeDate: Double {\n    get {\n      userDefaults.double(forKey: kLastNoticeDate)\n    }\n    set {\n      userDefaults.set(newValue, forKey: kLastNoticeDate)\n    }\n  }\n  \n  private let userDefaults: UserDefaults\n  \n  private let kIsInitialLaunch = \"kIsInitialLaunch\"\n  private let kLastNoticeDate = \"kLastNoticeDate\"\n  \n  public init(defaults: UserDefaults) {\n    self.userDefaults = defaults\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/Network/HTTPMethod.swift",
    "content": "import Foundation\n\npublic enum HTTPMethod: String, Encodable {\n  case get = \"GET\"\n  case post = \"POST\"\n  case put = \"PUT\"\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/Network/Network.swift",
    "content": "import Combine\nimport Foundation\n\npublic typealias QueryItems = [String: AnyHashable]\npublic typealias HTTPHeader = [String: String]\n\npublic protocol Request: Hashable {\n  associatedtype Output: Decodable\n  \n  var endpoint: URL { get }\n  var method: HTTPMethod { get }\n  var query: QueryItems { get }\n  var header: HTTPHeader { get }\n}\n\npublic protocol Network {\n  func send<T: Request>(_ request: T) -> AnyPublisher<Response<T.Output>, Error>\n}\n\npublic struct Response<T: Decodable> {\n  public let output: T\n  public let statusCode: Int\n  \n  public init(output: T, statusCode: Int) {\n    self.output = output\n    self.statusCode = statusCode\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/Network/NetworkError.swift",
    "content": "import Foundation\n\npublic enum NetworkError: Error {\n  case invalidURL(url: String?)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/NetworkImp/NetworkImp.swift",
    "content": "import Foundation\nimport Network\nimport Combine\n\npublic final class NetworkImp: Network {\n  \n  private let session: URLSession\n  \n  public init(\n    session: URLSession\n  ) {\n    self.session = session\n  }\n  \n  public func send<T>(_ request: T) -> AnyPublisher<Response<T.Output>, Error> where T: Request {\n    do {\n      let urlRequest = try RequestFactory(request: request).urlRequestRepresentation()\n      return session.dataTaskPublisher(for: urlRequest)\n        .tryMap { data, response in\n          let output = try JSONDecoder().decode(T.Output.self, from: data)\n          return Response(output: output, statusCode: (response as? HTTPURLResponse)?.statusCode ?? 0)\n        }\n        .eraseToAnyPublisher()\n    } catch {\n      return Fail(error: error).eraseToAnyPublisher()\n    }\n  }\n  \n}\n\nprivate final class RequestFactory<T: Request> {\n  \n  let request: T\n  private var urlComponents: URLComponents?\n  \n  init(request: T) {\n    self.request = request\n    self.urlComponents = URLComponents(url: request.endpoint, resolvingAgainstBaseURL: true)\n  }\n  \n  func urlRequestRepresentation() throws -> URLRequest {\n    switch request.method {\n    case .get:\n      return try makeGetRequest()\n    case .post:\n      return try makePostRequest()\n    case .put:\n      return try makePutRequest()\n    }\n  }\n  \n  private func makeGetRequest() throws -> URLRequest {\n    if request.query.isEmpty == false {\n      urlComponents?.queryItems = request.query.map { URLQueryItem(name: $0.key, value: \"\\($0.value)\") }\n    }\n    return try makeURLRequest()\n  }\n  \n  private func makePostRequest() throws -> URLRequest {\n    let body = try JSONSerialization.data(withJSONObject: request.query, options: [])\n    return try makeURLRequest(httpBody: body)\n  }\n  \n  private func makePutRequest() throws -> URLRequest {\n    if request.query.isEmpty == false {\n      urlComponents?.queryItems = request.query.map { URLQueryItem(name: $0.key, value: \"\\($0.value)\") }\n    }\n    return try makeURLRequest()\n  }\n  \n  private func makeURLRequest(httpBody: Data? = nil) throws -> URLRequest {\n    guard let url = urlComponents?.url else {\n      throw NetworkError.invalidURL(url: request.endpoint.absoluteString)\n    }\n    \n    var urlRequest = URLRequest(url: url)\n    request.header.forEach {\n      urlRequest.setValue($0.value, forHTTPHeaderField: $0.key)\n    }\n    urlRequest.httpMethod = request.method.rawValue\n    urlRequest.httpBody = httpBody\n    \n    return urlRequest\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/PlatformTestSupport/PlatformTestSupport.swift",
    "content": "import Foundation\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/RIBsTestSupport/RoutingMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport Combine\n\npublic final class RoutingMock: Routing {\n  \n  public var loadHandler: (() -> ())?\n  public var loadCallCount: Int = 0\n  public var attachChildHandler: ((_ child: Routing) -> ())?\n  public var attachChildCallCount: Int = 0\n  public var detachChildHandler: ((_ child: Routing) -> ())?\n  public var detachChildCallCount: Int = 0\n  \n  public var interactable: Interactable\n  \n  public var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  public var childrenSetCallCount = 0\n  \n  public init(\n    interactable: Interactable\n  ) {\n    self.interactable = interactable\n  }\n  \n  public func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n  \n  public func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n\n  public func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n  \n  public var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  public var lifecycleSubjectSetCallCount = 0\n  public var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/RIBsTestSupport/ViewControllableMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport UIKit\n\npublic final class ViewControllableMock: UIViewController, ViewControllable {\n  \n  public init() {\n    super.init(nibName: nil, bundle: nil)\n  }\n  \n  required init?(coder: NSCoder) {\n    fatalError(\"init(coder:) has not been implemented\")\n  }\n  \n  public var presentCallCount = 0\n  public override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {\n    presentCallCount += 1\n  }\n  \n  public var dismissCallCount = 0\n  public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {\n    dismissCallCount += 1\n  }\n\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/RIBsTestSupport/ViewableRoutingMock.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport Combine\n\nopen class ViewableRoutingMock: ViewableRouting {\n  // Variables\n  public var viewControllable: ViewControllable\n  public var interactable: Interactable { didSet { interactableSetCallCount += 1 } }\n  public var interactableSetCallCount = 0\n  public var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  public var childrenSetCallCount = 0\n  public var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  public var lifecycleSubjectSetCallCount = 0\n  public var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n\n  // Function Handlers\n  public var loadHandler: (() -> ())?\n  public var loadCallCount: Int = 0\n  public var attachChildHandler: ((_ child: Routing) -> ())?\n  public var attachChildCallCount: Int = 0\n  public var detachChildHandler: ((_ child: Routing) -> ())?\n  public var detachChildCallCount: Int = 0\n\n  public init(\n    interactable: Interactable,\n    viewControllable: ViewControllable\n  ) {\n    self.interactable = interactable\n    self.viewControllable = viewControllable\n  }\n\n  public func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n\n  public func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n\n  public func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/RIBsUtil/RIBs+Util.swift",
    "content": "import Foundation\nimport ModernRIBs\nimport UIKit\n\npublic enum DismissButtonType {\n  case back, close\n  \n  public var iconSystemName: String {\n    switch self {\n    case .back:\n      return \"chevron.backward\"\n    case .close:\n      return \"xmark\"\n    }\n  }\n}\n\npublic final class NavigationControllerable: ViewControllable {\n  \n  public var uiviewController: UIViewController { self.navigationController }\n  public let navigationController: UINavigationController\n  \n  public init(root: ViewControllable) {\n    let navigation = UINavigationController(rootViewController: root.uiviewController)\n    navigation.navigationBar.isTranslucent = false\n    navigation.navigationBar.backgroundColor = .white\n    navigation.navigationBar.scrollEdgeAppearance = navigation.navigationBar.standardAppearance\n    \n    self.navigationController = navigation\n  }\n}\n\npublic extension ViewControllable {\n  \n  func present(_ viewControllable: ViewControllable, animated: Bool, completion: (() -> Void)?) {\n    self.uiviewController.present(viewControllable.uiviewController, animated: animated, completion: completion)\n  }\n  \n  func dismiss(completion: (() -> Void)?) {\n    self.uiviewController.dismiss(animated: true, completion: completion)\n  }\n  \n  func pushViewController(_ viewControllable: ViewControllable, animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.pushViewController(viewControllable.uiviewController, animated: animated)\n    } else {\n      self.uiviewController.navigationController?.pushViewController(viewControllable.uiviewController, animated: animated)\n    }\n  }\n  \n  func popViewController(animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.popViewController(animated: animated)\n    } else {\n      self.uiviewController.navigationController?.popViewController(animated: animated)\n    }\n  }\n  \n  func popToRoot(animated: Bool) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.popToRootViewController(animated: animated)\n    } else {\n      self.uiviewController.navigationController?.popToRootViewController(animated: animated)\n    }\n  }\n  \n  func setViewControllers(_ viewControllerables: [ViewControllable]) {\n    if let nav = self.uiviewController as? UINavigationController {\n      nav.setViewControllers(viewControllerables.map(\\.uiviewController), animated: true)\n    } else {\n      self.uiviewController.navigationController?.setViewControllers(viewControllerables.map(\\.uiviewController), animated: true)\n    }\n  }\n  \n  var topViewControllable: ViewControllable {\n    var top: ViewControllable = self\n    \n    while let presented = top.uiviewController.presentedViewController as? ViewControllable {\n      top = presented\n    }\n    \n    return top\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/AdaptivePresentationControllerDelegate.swift",
    "content": "import UIKit\n\npublic protocol AdaptivePresentationControllerDelegate: AnyObject {\n  func presentationControllerDidDismiss()\n}\n\npublic final class AdaptivePresentationControllerDelegateProxy: NSObject, UIAdaptivePresentationControllerDelegate {\n  \n  public weak var delegate: AdaptivePresentationControllerDelegate?\n  \n  public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {\n    delegate?.presentationControllerDidDismiss()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/PushModalPresentationController.swift",
    "content": "import UIKit\n\npublic final class PushModalPresentationController: NSObject, UIViewControllerTransitioningDelegate {\n  \n  public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {\n    return PushModalPresentTransitioning()\n  }\n  \n  public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {\n    return PushModalDismissTransitioning()\n  }\n  \n}\n\nprivate final class PushModalPresentTransitioning: NSObject, UIViewControllerAnimatedTransitioning {\n  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {\n    return 0.25\n  }\n  \n  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {\n    guard let toViewController = transitionContext.viewController(forKey: .to) else {\n      return\n    }\n    \n    let containerView = transitionContext.containerView\n    let toView = transitionContext.view(forKey: .to)\n    \n    var toViewInitialFrame   = transitionContext.initialFrame(for: toViewController)\n    let toViewFinalFrame     = transitionContext.finalFrame(for: toViewController)\n    toView.map(containerView.addSubview)\n    \n    toViewInitialFrame.origin = CGPoint(x: containerView.bounds.maxX, y: containerView.bounds.minY)\n    toViewInitialFrame.size = toViewFinalFrame.size\n    toView?.frame = toViewInitialFrame\n    \n    UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.allowUserInteraction, .curveEaseOut], animations: {\n      toView?.frame = toViewFinalFrame\n    }) { _ in\n      let isCompleted = !transitionContext.transitionWasCancelled\n      transitionContext.completeTransition(isCompleted)\n    }\n  }\n  \n}\n\nprivate final class PushModalDismissTransitioning: NSObject, UIViewControllerAnimatedTransitioning {\n  \n  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {\n    return 0.25\n  }\n  \n  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {\n    guard let fromViewController = transitionContext.viewController(forKey: .from) else {\n      return\n    }\n    \n    let containerView = transitionContext.containerView\n    let toView = transitionContext.view(forKey: .to)\n    let fromView = transitionContext.view(forKey: .from)\n    var fromViewFinalFrame = transitionContext.finalFrame(for: fromViewController)\n    \n    toView.map(containerView.addSubview)\n    \n    if let fromView = fromView {\n      fromViewFinalFrame = fromView.frame.offsetBy(dx: fromView.frame.width, dy: 0)\n    }\n    \n    UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.allowUserInteraction, .curveEaseOut], animations: {\n      fromView?.frame = fromViewFinalFrame\n    }) { _ in\n      let isCompleted = !transitionContext.transitionWasCancelled\n      transitionContext.completeTransition(isCompleted)\n    }\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UIColor+Super.swift",
    "content": "import UIKit\n\npublic extension UIColor {\n  static let backgroundColor = UIColor(hex: \"#F1F5F9FF\")!\n  static let primaryRed = UIColor(hex: \"#eb445aff\")!\n}\n\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UIColor+Utils.swift",
    "content": "import UIKit\n\npublic extension UIColor {\n  convenience init?(hex: String) {\n    let r, g, b, a: CGFloat\n    \n    if hex.hasPrefix(\"#\") {\n      let start = hex.index(hex.startIndex, offsetBy: 1)\n      let hexColor = String(hex[start...])\n      \n      if hexColor.count == 8 {\n        let scanner = Scanner(string: hexColor)\n        var hexNumber: UInt64 = 0\n        \n        if scanner.scanHexInt64(&hexNumber) {\n          r = CGFloat((hexNumber & 0xff000000) >> 24) / 255\n          g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255\n          b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255\n          a = CGFloat(hexNumber & 0x000000ff) / 255\n          \n          self.init(red: r, green: g, blue: b, alpha: a)\n          return\n        }\n      }\n    }\n    \n    return nil\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UIImage+Utils.swift",
    "content": "import UIKit\n\npublic extension UIImage {\n  convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {\n    let rect = CGRect(origin: .zero, size: size)\n    UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)\n    color.setFill()\n    UIRectFill(rect)\n    let image = UIGraphicsGetImageFromCurrentImageContext()\n    UIGraphicsEndImageContext()\n    \n    guard let cgImage = image?.cgImage else { return nil }\n    \n    self.init(cgImage: cgImage)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UITableView+Utils.swift",
    "content": "import UIKit\n\npublic protocol Reusable: AnyObject {\n  static var reuseIdentifier: String { get }\n}\n\npublic extension Reusable {\n  static var reuseIdentifier: String {\n    return String(describing: self)\n  }\n}\n\nextension UITableViewCell: Reusable {}\n\npublic extension UITableView {\n  \n  func register<T: UITableViewCell>(cellType: T.Type) {\n    self.register(cellType, forCellReuseIdentifier: T.reuseIdentifier)\n  }\n  \n  func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T {\n    guard let cell = self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {\n      fatalError(\"Failed to dequeue reusable cell\")\n    }\n    return cell\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UIView+Utils.swift",
    "content": "import UIKit\n\npublic extension UIView {\n  func addShadowWithRoundedCorners(\n    _ radius: CGFloat = 16,\n    shadowColor: CGColor = UIColor.black.cgColor,\n    opacity: Float = 0.1\n  ) {\n    self.layer.cornerCurve = .continuous\n    self.layer.masksToBounds = false\n    self.layer.shadowColor = shadowColor\n    self.layer.shadowOffset = CGSize(width: 0, height: 0)\n    self.layer.shadowOpacity = opacity\n    self.layer.shadowRadius = 2.5\n    self.layer.cornerRadius = radius\n  }\n  \n  func roundCorners(\n    _ radius: CGFloat = 16\n  ) {\n    self.layer.cornerCurve = .continuous\n    self.layer.cornerRadius = radius\n    self.clipsToBounds = true\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Platform/Sources/SuperUI/UIViewController+Utils.swift",
    "content": "import UIKit\nimport RIBsUtil\n\npublic extension UIViewController {\n  func setupNavigationItem(with buttonType: DismissButtonType, target: Any?, action: Selector?) {\n    navigationItem.leftBarButtonItem = UIBarButtonItem(\n      image: UIImage(\n        systemName: buttonType.iconSystemName,\n        withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n      ),\n      style: .plain,\n      target: target,\n      action: action\n    )\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"Profile\",\n  platforms: [.iOS(.v14)],\n  products: [\n    // Products define the executables and libraries a package produces, and make them visible to other packages.\n    .library(\n      name: \"ProfileHome\",\n      targets: [\"ProfileHome\"]\n    ),\n  ],\n  dependencies: [\n    .package(name: \"ModernRIBs\", url: \"https://github.com/DevYeom/ModernRIBs\", .exact(\"1.0.1\")),\n  ],\n  targets: [\n    // Targets are the basic building blocks of a package. A target can define a module or a test suite.\n    // Targets can depend on other targets in this package, and on products in packages this package depends on.\n    .target(\n      name: \"ProfileHome\",\n      dependencies: [\n        \"ModernRIBs\"\n      ]\n    ),\n  ]\n)\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/README.md",
    "content": "# Profile\n\nA description of this package.\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/Sources/ProfileHome/ProfileHomeBuilder.swift",
    "content": "import ModernRIBs\n\npublic protocol ProfileHomeDependency: Dependency {\n}\n\nfinal class ProfileHomeComponent: Component<ProfileHomeDependency> {\n}\n\n// MARK: - Builder\n\npublic protocol ProfileHomeBuildable: Buildable {\n  func build(withListener listener: ProfileHomeListener) -> ViewableRouting\n}\n\npublic final class ProfileHomeBuilder: Builder<ProfileHomeDependency>, ProfileHomeBuildable {\n  \n  public override init(dependency: ProfileHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: ProfileHomeListener) -> ViewableRouting {\n    let _ = ProfileHomeComponent(dependency: dependency)\n    let viewController = ProfileHomeViewController()\n    let interactor = ProfileHomeInteractor(presenter: viewController)\n    interactor.listener = listener\n    return ProfileHomeRouter(interactor: interactor, viewController: viewController)\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/Sources/ProfileHome/ProfileHomeInteractor.swift",
    "content": "import ModernRIBs\n\nprotocol ProfileHomeRouting: ViewableRouting {\n}\n\nprotocol ProfileHomePresentable: Presentable {\n  var listener: ProfileHomePresentableListener? { get set }\n}\n\npublic protocol ProfileHomeListener: AnyObject {\n}\n\nfinal class ProfileHomeInteractor: PresentableInteractor<ProfileHomePresentable>, ProfileHomeInteractable, ProfileHomePresentableListener {\n  \n  weak var router: ProfileHomeRouting?\n  weak var listener: ProfileHomeListener?\n  \n  override init(presenter: ProfileHomePresentable) {\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/Sources/ProfileHome/ProfileHomeRouter.swift",
    "content": "import ModernRIBs\n\nprotocol ProfileHomeInteractable: Interactable {\n  var router: ProfileHomeRouting? { get set }\n  var listener: ProfileHomeListener? { get set }\n}\n\nprotocol ProfileHomeViewControllable: ViewControllable {\n}\n\nfinal class ProfileHomeRouter: ViewableRouter<ProfileHomeInteractable, ProfileHomeViewControllable>, ProfileHomeRouting {\n  \n  override init(interactor: ProfileHomeInteractable, viewController: ProfileHomeViewControllable) {\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Profile/Sources/ProfileHome/ProfileHomeViewController.swift",
    "content": "import ModernRIBs\nimport UIKit\n\nprotocol ProfileHomePresentableListener: AnyObject {\n}\n\nfinal class ProfileHomeViewController: UIViewController, ProfileHomePresentable, ProfileHomeViewControllable {\n  \n  weak var listener: ProfileHomePresentableListener?\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private let label: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    return label\n  }()\n  \n  func setupViews() {\n    tabBarItem = UITabBarItem(title: \"프로필\", image: UIImage(systemName: \"person\"), selectedImage: UIImage(systemName: \"person.fill\"))\n    label.text = \"Profile Home\"\n    view.backgroundColor = .systemTeal\n    view.addSubview(label)\n    NSLayoutConstraint.activate([\n      label.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n      label.centerYAnchor.constraint(equalTo: view.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Samples/TestUtil.swift",
    "content": "import Foundation\n\nenum TestUtilError: Error {\n  case fileNotFound\n}\n\nclass TestUtil {\n  static func path(for fileName: String, in bundleClass: AnyClass) throws -> String {\n    if let path = Bundle(for: bundleClass).path(forResource: fileName, ofType: nil) {\n      return path\n    } else {\n      throw TestUtilError.fileNotFound\n    }\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Samples/TopupDependencyMock.swift",
    "content": "//\n//  File.swift\n//  \n//\n//  Created by Soojin Ro on 2021/10/04.\n//\n\n@testable import TopupImp\nimport Foundation\nimport CombineUtil\nimport FinanceEntity\nimport FinanceRepositoryTestSupport\nimport FinanceRepository\nimport Combine\nimport ModernRIBs\nimport RIBsUtil\nimport Topup\nimport SuperUI\n\nfinal class TopupDependencyMock: TopupInteractorDependency {\n  var cardOnFileRepository: CardOnFileRepository = CardOnFileRepositoryMock()\n  var paymentMethodStream: CurrentValuePublisher<PaymentMethod> = .init(\n    PaymentMethod(id: \"\", name: \"\", digits: \"\", color: \"\", isPrimary: false)\n  )\n}\n\nfinal class TopupRoutingMock: TopupRouting {\n  \n  var attachAddPaymentMethodCallCount = 0\n  var attachAddPaymentMethodCloseButtonType: DismissButtonType?\n  func attachAddPaymentMethod(closeButtonType: DismissButtonType) {\n    attachAddPaymentMethodCallCount += 1\n    attachAddPaymentMethodCloseButtonType = closeButtonType\n  }\n  \n  var detachAddPaymentMethodCallCount = 0\n  func detachAddPaymentMethod() {\n    detachAddPaymentMethodCallCount += 1\n  }\n  \n  var attachEnterAmountCallCount = 0\n  func attachEnterAmount() {\n    attachEnterAmountCallCount += 1\n  }\n  \n  var detachEnterAmountCallCount = 0\n  func detachEnterAmount() {\n    detachEnterAmountCallCount += 1\n  }\n  \n  var attachCardOnFileCallCount = 0\n  var attachCardOnFileCallCountPaymentMethods: [PaymentMethod]?\n  func attachCardOnFile(paymentMethods: [PaymentMethod]) {\n    attachCardOnFileCallCount += 1\n  }\n  \n  var detachCardOnFileCallCount = 0\n  func detachCardOnFile() {\n    detachCardOnFileCallCount += 1\n  }\n  \n  var popToRootCallCount = 0\n  func popToRoot() {\n    popToRootCallCount += 1\n  }\n  \n  // Variables\n  var interactable: Interactable { didSet { interactableSetCallCount += 1 } }\n  var interactableSetCallCount = 0\n  var children: [Routing] = [Routing]() { didSet { childrenSetCallCount += 1 } }\n  var childrenSetCallCount = 0\n  var lifecycleSubject = PassthroughSubject<RouterLifecycle, Never>() {\n    didSet {\n      lifecycleSubjectSetCallCount += 1\n    }\n  }\n  var lifecycleSubjectSetCallCount = 0\n  var lifecycle: AnyPublisher<RouterLifecycle, Never> { return lifecycleSubject.eraseToAnyPublisher() }\n  \n  // Function Handlers\n  var loadHandler: (() -> ())?\n  var loadCallCount: Int = 0\n  var attachChildHandler: ((_ child: Routing) -> ())?\n  var attachChildCallCount: Int = 0\n  var detachChildHandler: ((_ child: Routing) -> ())?\n  var detachChildCallCount: Int = 0\n  \n  init(\n    interactable: Interactable\n  ) {\n    self.interactable = interactable\n  }\n  \n  var cleanupViewsCallCount = 0\n  func cleanupViews() {\n    cleanupViewsCallCount += 1\n  }\n  \n  func load() {\n    loadCallCount += 1\n    if let loadHandler = loadHandler {\n      return loadHandler()\n    }\n  }\n  \n  func attachChild(_ child: Routing) {\n    attachChildCallCount += 1\n    if let attachChildHandler = attachChildHandler {\n      return attachChildHandler(child)\n    }\n  }\n  \n  func detachChild(_ child: Routing) {\n    detachChildCallCount += 1\n    if let detachChildHandler = detachChildHandler {\n      return detachChildHandler(child)\n    }\n  }\n}\n\nfinal class TopupInteractableMock: TopupInteractable {\n  var router: TopupRouting?\n  var listener: TopupListener?\n  var presentationDelegateProxy = AdaptivePresentationControllerDelegateProxy()\n  \n  var addPaymentMethodDidTapCloseCallCount = 0\n  func addPaymentMethodDidTapClose() {\n    addPaymentMethodDidTapCloseCallCount += 1\n  }\n  \n  var addPaymentMethodDidAddCardCallCount = 0\n  var addPaymentMethodDidAddCardPaymentMethod: PaymentMethod?\n  func addPaymentMethodDidAddCard(paymentMethod: PaymentMethod) {\n    addPaymentMethodDidAddCardCallCount += 1\n    addPaymentMethodDidAddCardPaymentMethod = paymentMethod\n  }\n  \n  var enterAmountDidTapCloseCallCount = 0\n  func enterAmountDidTapClose() {\n    enterAmountDidTapCloseCallCount += 1\n  }\n  \n  var enterAmountDidTapPaymentMethodCallCount = 0\n  func enterAmountDidTapPaymentMethod() {\n    enterAmountDidTapPaymentMethodCallCount += 1\n  }\n  \n  var enterAmountDidFinishTopupCallCount = 0\n  func enterAmountDidFinishTopup() {\n    enterAmountDidFinishTopupCallCount += 1\n  }\n  \n  var cardOnFileDidTapCloseCallCount = 0\n  func cardOnFileDidTapClose() {\n    cardOnFileDidTapCloseCallCount += 1\n  }\n  \n  var cardOnFileDidTapAddCardCallCount = 0\n  func cardOnFileDidTapAddCard() {\n    cardOnFileDidTapAddCardCallCount += 1\n  }\n  \n  var cardOnFileDidSelectCardCallCount = 0\n  var cardOnFileDidSelectCardIndex: Int?\n  func cardOnFileDidSelectCard(at index: Int) {\n    cardOnFileDidSelectCardCallCount += 1\n    cardOnFileDidSelectCardIndex = index\n  }\n  \n  func activate() {\n    \n  }\n  \n  func deactivate() {\n    \n  }\n  \n  var isActive: Bool { isActiveSubject.value }\n  var isActiveStream: AnyPublisher<Bool, Never> { isActiveSubject.eraseToAnyPublisher() }\n  private let isActiveSubject = CurrentValueSubject<Bool, Never>(false)\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/AppDelegate.swift",
    "content": "import UIKit\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  public var window: UIWindow?\n\n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    \n    let window = UIWindow(frame: UIScreen.main.bounds)\n    self.window = window\n    \n    return true\n  }\n\n}\n\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "completed/MiniSuperApp/TestHost/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/.gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"Transport\",\n  platforms: [.iOS(.v14)],\n  products: [\n    .library(\n      name: \"TransportHome\",\n      targets: [\"TransportHome\"]\n    ),\n    .library(\n      name: \"TransportHomeImp\",\n      targets: [\"TransportHomeImp\"]\n    ),\n  ],\n  dependencies: [\n    .package(name: \"ModernRIBs\", url: \"https://github.com/DevYeom/ModernRIBs\", .exact(\"1.0.1\")),\n    .package(path: \"../Finance\"),\n    .package(path: \"../Platform\")\n  ],\n  targets: [\n    .target(\n      name: \"TransportHome\",\n      dependencies: [\n        \"ModernRIBs\",\n      ]\n    ),\n    .target(\n      name: \"TransportHomeImp\",\n      dependencies: [\n        \"ModernRIBs\",\n        \"TransportHome\",\n        .product(name: \"FinanceRepository\", package: \"Finance\"),\n        .product(name: \"Topup\", package: \"Finance\"),\n        .product(name: \"SuperUI\", package: \"Platform\"),\n      ],\n      resources: [\n        .process(\"Resources\"),\n      ]\n    ),\n  ]\n)\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/README.md",
    "content": "# Transport\n\nA description of this package.\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHome/TransportHomeInterface.swift",
    "content": "import Foundation\nimport ModernRIBs\n\npublic protocol TransportHomeBuildable: Buildable {\n  func build(withListener listener: TransportHomeListener) -> ViewableRouting\n}\n\npublic protocol TransportHomeListener: AnyObject {\n  func transportHomeDidTapClose()\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/Formatter.swift",
    "content": "import Foundation\n\nstruct Formatter {\n  static let balanceFormatter: NumberFormatter = {\n    let formatter = NumberFormatter()\n    formatter.numberStyle = .decimal\n    return formatter\n  }()\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/TransportHomeBuilder.swift",
    "content": "import ModernRIBs\nimport FinanceRepository\nimport CombineUtil\nimport Topup\nimport TransportHome\n\npublic protocol TransportHomeDependency: Dependency {\n  var cardOnFileRepository: CardOnFileRepository { get }\n  var superPayRepository: SuperPayRepository { get }\n  var topupBuildable: TopupBuildable { get }\n}\n\nfinal class TransportHomeComponent: Component<TransportHomeDependency>, TransportHomeInteractorDependency {\n  let topupBaseViewController: ViewControllable\n  var cardOnFileRepository: CardOnFileRepository { dependency.cardOnFileRepository }\n  var superPayRepository: SuperPayRepository { dependency.superPayRepository }\n  var superPayBalance: ReadOnlyCurrentValuePublisher<Double> { superPayRepository.balance }\n  var topupBuildable: TopupBuildable { dependency.topupBuildable }\n  \n  init(\n    dependency: TransportHomeDependency,\n    topupBaseViewController: ViewControllable\n  ) {\n    self.topupBaseViewController = topupBaseViewController\n    super.init(dependency: dependency)\n  }\n}\n\n// MARK: - Builder\npublic final class TransportHomeBuilder: Builder<TransportHomeDependency>, TransportHomeBuildable {\n  \n  override public init(dependency: TransportHomeDependency) {\n    super.init(dependency: dependency)\n  }\n  \n  public func build(withListener listener: TransportHomeListener) -> ViewableRouting {\n    let viewController = TransportHomeViewController()\n    let component = TransportHomeComponent(dependency: dependency, topupBaseViewController: viewController)\n    \n    let interactor = TransportHomeInteractor(presenter: viewController, dependency: component)\n    interactor.listener = listener\n    \n    return TransportHomeRouter(\n      interactor: interactor,\n      viewController: viewController,\n      topupBuildable: component.topupBuildable\n    )\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/TransportHomeInteractor.swift",
    "content": "import ModernRIBs\nimport Combine\nimport Foundation\nimport CombineUtil\nimport TransportHome\n\nprotocol TransportHomeRouting: ViewableRouting {\n  func attachTopup()\n  func detachTopup()\n}\n\nprotocol TransportHomePresentable: Presentable {\n  var listener: TransportHomePresentableListener? { get set }\n  \n  func setSuperPayBalance(_ balance: String)\n}\n\nprotocol TransportHomeInteractorDependency {\n  var superPayBalance: ReadOnlyCurrentValuePublisher<Double> { get }\n}\n\nfinal class TransportHomeInteractor: PresentableInteractor<TransportHomePresentable>, TransportHomeInteractable, TransportHomePresentableListener {\n  \n  weak var router: TransportHomeRouting?\n  weak var listener: TransportHomeListener?\n  \n  private var cancellables: Set<AnyCancellable>\n  \n  private let ridePrice: Double = 18000\n  \n  private let dependency: TransportHomeInteractorDependency\n  \n  init(\n    presenter: TransportHomePresentable,\n    dependency: TransportHomeInteractorDependency\n  ) {\n    self.dependency = dependency\n    self.cancellables = .init()\n    super.init(presenter: presenter)\n    presenter.listener = self\n  }\n  \n  override func didBecomeActive() {\n    super.didBecomeActive()\n    \n    dependency.superPayBalance\n      .receive(on: DispatchQueue.main)\n      .sink { [weak self] balance in\n        if let balanceText = Formatter.balanceFormatter.string(from: NSNumber(value: balance)) {\n          self?.presenter.setSuperPayBalance(balanceText)\n        }\n      }\n      .store(in: &cancellables)\n  }\n  \n  override func willResignActive() {\n    super.willResignActive()\n    // TODO: Pause any business logic.\n  }\n  \n  func didTapBack() {\n    listener?.transportHomeDidTapClose()\n  }\n  \n  func didTapRideConfirmButton() {\n    if dependency.superPayBalance.value < ridePrice {\n      router?.attachTopup()\n    } else {\n      print(\"Success\")\n    }\n  }\n  \n  func topupDidClose() {\n    router?.detachTopup()\n  }\n  \n  func topupDidFinish() {\n    router?.detachTopup()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/TransportHomeRouter.swift",
    "content": "import ModernRIBs\nimport Topup\nimport TransportHome\n\nprotocol TransportHomeInteractable: Interactable, TopupListener {\n  var router: TransportHomeRouting? { get set }\n  var listener: TransportHomeListener? { get set }\n}\n\nprotocol TransportHomeViewControllable: ViewControllable {\n}\n\nfinal class TransportHomeRouter: ViewableRouter<TransportHomeInteractable, TransportHomeViewControllable>, TransportHomeRouting {\n  \n  private let topupBuildable: TopupBuildable\n  private var topupRouting: Routing?\n  \n  init(\n    interactor: TransportHomeInteractable,\n    viewController: TransportHomeViewControllable,\n    topupBuildable: TopupBuildable\n  ) {\n    self.topupBuildable = topupBuildable\n    super.init(interactor: interactor, viewController: viewController)\n    interactor.router = self\n  }\n  \n  func attachTopup() {\n    if topupRouting != nil {\n      return\n    }\n    \n    let router = topupBuildable.build(withListener: interactor)\n    self.topupRouting = router\n    attachChild(router)\n  }\n  \n  func detachTopup() {\n    guard let router = topupRouting else {\n      return\n    }\n    \n    detachChild(router)\n    self.topupRouting = nil\n  }\n  \n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/TransportHomeViewController.swift",
    "content": "import ModernRIBs\nimport SuperUI\nimport UIKit\n\nprotocol TransportHomePresentableListener: AnyObject {\n  func didTapBack()\n  func didTapRideConfirmButton()\n}\n\nfinal class TransportHomeViewController: UIViewController, TransportHomePresentable, TransportHomeViewControllable {\n  \n  weak var listener: TransportHomePresentableListener?\n  \n  private let mapView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFill\n    imageView.image = UIImage(named: \"map_seoul\", in: .module, with: nil)\n    return imageView\n  }()\n  \n  private let searchView: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.addShadowWithRoundedCorners(8)\n    view.backgroundColor = .white\n    return view\n  }()\n  \n  private let departureLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"우리집\"\n    return label\n  }()\n  \n  private let destinationLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"회사\"\n    return label\n  }()\n  \n  private let arrowImageView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.tintColor = .black\n    imageView.image = UIImage(\n      systemName: \"arrow.right\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n    )\n    return imageView\n  }()\n  \n  private let rideTypeView: RideTypeView = {\n    let view = RideTypeView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    return view\n  }()\n  \n  private let superPayView: SuperPayView = {\n    let view = SuperPayView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    return view\n  }()\n  \n  private lazy var backButton: UIButton = {\n    let button = UIButton()\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.backgroundColor = .white\n    button.roundCorners(25)\n    button.tintColor = .black\n    button.setImage(\n      UIImage(\n        systemName: \"chevron.backward\",\n        withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n      ),\n      for: .normal\n    )\n    button.addTarget(self, action: #selector(backButtonDidTap), for: .touchUpInside)\n    return button\n  }()\n  \n  private let rideTypeStackView: UIStackView = {\n    let stack = UIStackView()\n    \n    return stack\n  }()\n  \n  private let paymentStackView: UIStackView = {\n    let stack = UIStackView()\n    \n    return stack\n  }()\n  \n  private let rideInfoPane: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.backgroundColor = .white\n    view.addShadowWithRoundedCorners()\n    return view\n  }()\n  \n  private lazy var rideConfirmButton: UIButton = {\n    let button = UIButton(type: .system)\n    button.translatesAutoresizingMaskIntoConstraints = false\n    button.setTitle(\"슈퍼택시 호출하기\", for: .normal)\n    button.backgroundColor = .primaryRed\n    button.tintColor = .white\n    button.addTarget(self, action: #selector(didTapRideConfirmButton), for: .touchUpInside)\n    button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)\n    return button\n  }()\n  \n  private let separatorView: UIView = {\n    let view = UIView()\n    view.translatesAutoresizingMaskIntoConstraints = false\n    view.backgroundColor = .systemGray6\n    return view\n  }()\n  \n  func setSuperPayBalance(_ balanceText: String) {\n    superPayView.setBalanceText(\"잔고: \\(balanceText)원\")\n  }\n  \n  init() {\n    super.init(nibName: nil, bundle: nil)\n    \n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    \n    setupViews()\n  }\n  \n  private func setupViews() {\n    view.addSubview(mapView)\n    view.addSubview(searchView)\n    searchView.addSubview(arrowImageView)\n    searchView.addSubview(departureLabel)\n    searchView.addSubview(destinationLabel)\n    view.addSubview(backButton)\n    view.addSubview(rideInfoPane)\n    rideInfoPane.addSubview(rideTypeView)\n    rideInfoPane.addSubview(superPayView)\n    rideInfoPane.addSubview(separatorView)\n    rideInfoPane.addSubview(rideConfirmButton)\n    \n    NSLayoutConstraint.activate([\n      mapView.topAnchor.constraint(equalTo: view.topAnchor),\n      mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      mapView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7),\n      \n      searchView.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 10),\n      searchView.centerYAnchor.constraint(equalTo: backButton.centerYAnchor),\n      searchView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),\n      searchView.heightAnchor.constraint(equalToConstant: 50),\n      \n      departureLabel.leadingAnchor.constraint(equalTo: searchView.leadingAnchor, constant: 60),\n      departureLabel.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      destinationLabel.trailingAnchor.constraint(equalTo: searchView.trailingAnchor, constant: -60),\n      destinationLabel.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      rideInfoPane.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n      rideInfoPane.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n      rideInfoPane.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n      rideInfoPane.topAnchor.constraint(equalTo: mapView.bottomAnchor),\n      \n      backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),\n      backButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),\n      backButton.widthAnchor.constraint(equalToConstant: 50),\n      backButton.heightAnchor.constraint(equalToConstant: 50),\n      \n      arrowImageView.centerXAnchor.constraint(equalTo: searchView.centerXAnchor),\n      arrowImageView.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),\n      \n      rideTypeView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      rideTypeView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      rideTypeView.topAnchor.constraint(equalTo: rideInfoPane.topAnchor, constant: 10),\n      rideTypeView.heightAnchor.constraint(equalToConstant: 70),\n      \n      separatorView.topAnchor.constraint(equalTo: rideTypeView.bottomAnchor),\n      separatorView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor),\n      separatorView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor),\n      separatorView.heightAnchor.constraint(equalToConstant: 1),\n      \n      superPayView.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      superPayView.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      superPayView.topAnchor.constraint(equalTo: separatorView.bottomAnchor, constant: 0),\n      superPayView.bottomAnchor.constraint(equalTo: rideConfirmButton.topAnchor),\n      \n      rideConfirmButton.leadingAnchor.constraint(equalTo: rideInfoPane.leadingAnchor, constant: 30),\n      rideConfirmButton.trailingAnchor.constraint(equalTo: rideInfoPane.trailingAnchor, constant: -30),\n      rideConfirmButton.bottomAnchor.constraint(equalTo: rideInfoPane.safeAreaLayoutGuide.bottomAnchor, constant: -20),\n      rideConfirmButton.heightAnchor.constraint(equalToConstant: 60)\n    ])\n  }\n  \n  @objc\n  private func backButtonDidTap() {\n    listener?.didTapBack()\n  }\n  \n  @objc\n  private func didTapRideConfirmButton() {\n    listener?.didTapRideConfirmButton()\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/Views/RideTypeView.swift",
    "content": "import UIKit\n\nfinal class RideTypeView: UIView {\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.contentMode = .scaleAspectFit\n    imageView.tintColor = .black\n    imageView.image = UIImage(\n      systemName: \"bolt.car\",\n      withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .semibold)\n    )\n    return imageView\n  }()\n  \n  private let rideTypeNameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"슈퍼전기차 택시\"\n    return label\n  }()\n  \n  private let priceLabel: UILabel = {\n    let label = UILabel()\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.text = \"18,000원\"\n    return label\n  }()\n  \n  init() {\n    super.init(frame: .zero)\n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(thumbnailView)\n    addSubview(priceLabel)\n    addSubview(rideTypeNameLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.leadingAnchor.constraint(equalTo: self.leadingAnchor),\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      thumbnailView.widthAnchor.constraint(equalToConstant: 40),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 40),\n      \n      rideTypeNameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      rideTypeNameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 10),\n      \n      priceLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      priceLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor)\n    ])\n  }\n}\n"
  },
  {
    "path": "completed/MiniSuperApp/Transport/Sources/TransportHomeImp/Views/SuperPayView.swift",
    "content": "import UIKit\n\nfinal class SuperPayView: UIView {\n  \n  private let thumbnailView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    imageView.backgroundColor = .systemBlue\n    imageView.roundCorners(4)\n    return imageView\n  }()\n  \n  private let nameLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"슈퍼페이\"\n    return label\n  }()\n  \n  private let balanceLabel: UILabel = {\n    let label = UILabel()\n    label.translatesAutoresizingMaskIntoConstraints = false\n    label.font = UIFont.systemFont(ofSize: 18, weight: .medium)\n    label.text = \"---원\"\n    return label\n  }()\n  \n  func setBalanceText(_ text: String) {\n    balanceLabel.text = text\n  }\n  \n  init() {\n    super.init(frame: .zero)\n    setupViews()\n  }\n  \n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    setupViews()\n  }\n  \n  private func setupViews() {\n    addSubview(thumbnailView)\n    addSubview(nameLabel)\n    addSubview(balanceLabel)\n    \n    NSLayoutConstraint.activate([\n      thumbnailView.widthAnchor.constraint(equalToConstant: 46),\n      thumbnailView.heightAnchor.constraint(equalToConstant: 34),\n      thumbnailView.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      \n      nameLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n      nameLabel.leadingAnchor.constraint(equalTo: thumbnailView.trailingAnchor, constant: 10),\n      \n      balanceLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor),\n      balanceLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),\n    ])\n  }\n}\n"
  }
]