//
//  PostModelTests.swift
//  SwiftyPress ModelTests
//
//  Created by Basem Emara on 2019-05-11.
//  Copyright © 2019 Zamzam Inc. All rights reserved.
//

#if !os(watchOS)
import XCTest
import SwiftyPress
import ZamzamCore

final class PostModelTests: XCTestCase {
    
}

extension PostModelTests {
    
    func testDecoding() throws {
        let model = try JSONDecoder.default.decode(Post.self, fromJSON: #fileID)
        
        XCTAssertEqual(model.id, 41294)
        XCTAssertEqual(model.title, "So Swift, So Clean Architecture for iOS")
        XCTAssertEqual(model.slug, "swift-clean-architecture")
        XCTAssertEqual(model.type, "post")
        XCTAssertEqual(model.excerpt, "The topic of iOS app architecture has evolved a long way from MVC. Unfortunately, the conversation becomes a frameworks and patterns war. The reality is: Rx is a framework; MVVM is a presentation pattern; and so on. Frameworks and patterns always come and go, but architectures are timeless. In this post, we will examine the Clean Architecture for building scalable apps in iOS.")
        XCTAssertEqual(model.content, "<blockquote><p>You are not paid to code; you are paid to create solutions.</p></blockquote>\n<p>The topic of iOS app architecture has evolved a long way from MVC. Unfortunately, the conversation becomes a frameworks and patterns war. The reality is: Rx is a framework; MVVM is a presentation pattern; and so on. Frameworks and patterns always come and go, but architectures are timeless. In this post, we will examine the Clean Architecture for building scalable apps in iOS.</p>\n<h2>What About Bob?</h2>\n<p>The Clean Architecture was coined by Uncle Bob almost 20 years ago. It is independent of platforms, frameworks, databases, or UI. It is testable. It&#8217;s a way of life (ok, software life). Sound good?</p>\n<p>If you have an hour to spare for learning, I highly suggest watching this lecture called <strong>The Principles of Clean Architecture by Uncle Bob Martin</strong> <em>(skip to 10:00 if you don&#8217;t want the biology lesson, although still interesting)</em>:</p>\n<p><iframe width=\"500\" height=\"281\" src=\"https://www.youtube.com/embed/o_TH-Y78tt4?feature=oembed\" frameborder=\"0\" allow=\"autoplay; encrypted-media\" allowfullscreen></iframe></p>\n<h2>A Thing About Screaming Architecture</h2>\n<p>Regarding project structure, let&#8217;s first get the feature vs. type debate out the way. The application should scream what it does without any IDE or explanation at all. Anyone should be able to open up the file system and know exactly what the application does. Take a look at the two apps below:</p>\n<p><img src=\"http://staging1.basememara.com/wp-content/uploads/2018/04/feature-vs-type-project.png\" alt=\"\" width=\"266\" height=\"568\" class=\"aligncenter size-full wp-image-41299\" /></p>\n<p>In the first app, all it tells me is that it&#8217;s an MVC project and has something to do with products and users. Why do I have to know MVC to figure out what it does? I have to compile and run it in my mind to figure out what it does. What&#8217;s worse, I have to expand all the folders to start investigating. In a complex app, imagine hundreds of controllers or views polluting your eyes. Maintaining and debugging are nightmare too, jumping back and forth between folders is like a game of ping pong.</p>\n<p>Now take a look at the second app. Right off the bat, I can tell it lists products, displays it, and shows a user&#8217;s profile. I don&#8217;t have to expand the folders to know what it is. I don&#8217;t have to know what framework or pattern it&#8217;s using. I don&#8217;t even have to know what language it&#8217;s written in!</p>\n<p>Another way to look at it is would you rather organize drawers by just throwing everything into three drawers, or is it better to have several small well-defined and well-labeled drawers?</p>\n<p>Ok let&#8217;s move on&#8230;</p>\n<h2>The Anatomy of the Clean Architecture</h2>\n<p>There are actually many variations and flavours of the Clean Architecture. You&#8217;ve probably heard of <a href=\"https://mutualmobile.com/posts/meet-viper-fast-agile-non-lethal-ios-architecture-framework\" target=\"_blank\" rel=\"noopener\">VIPER</a> or <a href=\"https://clean-swift.com\" target=\"_blank\" rel=\"noopener\">Clean Swift</a>. When you jump into other platforms like Android or .NET, there even exists more flavours. The one I will show you in this post was <em>heavily</em> inspired by Clean Swift. I put my own spin to it after battle-testing it and maintaining it across several teams, platforms, and environments.</p>\n<p>To get started, we have to get some terminology out the way. Below are a list of components in the architecture:</p>\n<ul>\n<li><strong>View:</strong> The interface where user interactions occur, such as the <code>Storyboard</code> or <code>XIB</code>.</li>\n<li><strong>Controller:</strong> The layer which binds the view to code, this being the <code>UIViewController</code> in our case.</li>\n<li><strong>Interactor:</strong> The business logic layer where the controller must send requests through.</li>\n<li><strong>Presenter:</strong> The layer that formats the response from the Interactor and sends it back to the controller.</li>\n<li><strong>Router:</strong> The layer that transports the user to another use case scene, usually an event that occurs in the controller.</li>\n</ul>\n<p>The core pieces of the architecture is the <em>Controller, Interactor, and Presenter</em>. The important thing to notice is that it has a uni-directional flow! This tremendously reduces complexity and makes it easy to manage. Since a picture is worth a thousand words, I have summed up the entire architecture in a single diagram:</p>\n<p><img src=\"http://staging1.basememara.com/wp-content/uploads/2018/04/Clean-Architecture-Cycle-2-500x518.png\" alt=\"\" width=\"500\" height=\"518\" class=\"aligncenter wp-image-41346 size-medium\" /></p>\n<p>Follow the flow below to understand how the architecture works:</p>\n<ol>\n<li>User interacts with <strong><em>View</em></strong></li>\n<li><strong><em>Controller</em></strong> event fires, builds a request model, and calls the <strong><em>Interactor</em></strong></li>\n<li>The <strong><em>Interactor</em></strong> calls the underlying core library with all your workers to access the data (could even subscribe to an observable here if needed)</li>\n<li>The <strong><em>Interactor</em></strong> receives data from the worker and wraps it in a response model to send to the <strong><em>Presenter</em></strong></li>\n<li>The <strong><em>Presenter</em></strong> calls app-level helpers if needed to format or localize the response and builds a view model to send to the <strong><em>Controller</em></strong></li>\n<li>The <strong><em>Controller</em></strong> receives the view model in a function and binds it to the <strong><em>View</em></strong></li>\n<li>Repeat the cycle&#8230;</li>\n</ol>\n<p>Also notice the architecture is cross-platform. It doesn&#8217;t care that the view is served by an iOS app, Android app, web browser, or even a REST API endpoint. Once it gets past the view, everything should be identical across platforms. In fact, an iOS and Android app can be eerily similar with just the <code>Storyboard</code> vs <code>Layout</code> being the difference. Even the iOS <code>UIViewController</code> and Android <code>Activity</code> code would almost be identical except for the binding logic. This makes collaboration and bug fixing tight between teams!</p>\n<h2>The Details</h2>\n<p><img src=\"http://staging1.basememara.com/wp-content/uploads/2018/04/show_me_the_code.jpg\" alt=\"\" width=\"306\" height=\"165\" class=\"aligncenter size-full wp-image-41304\" /></p>\n<p>It&#8217;s time to see how this plays out in code. We can do this by first examining the protocols that is the blueprint for a use case; this is important so the boundaries are defined and components are decoupled:</p>\n<pre class=\"lang:swift decode:true\">protocol ListProductsDisplayable: class { // View Controller\r\n    func displayFetchedProducts(with viewModel: ListProductsModels.ViewModel)\r\n    func display(error: AppModels.Error)\r\n}\r\n\r\nprotocol ListProductsBusinessLogic { // Interactor\r\n    func fetchProducts(with request: ListProductsModels.FetchRequest)\r\n}\r\n\r\nprotocol ListProductsPresentable { // Presenter\r\n    func presentFetchedProducts(for response: ListProductsModels.Response)\r\n    func presentFetchedProducts(error: DataError)\r\n}\r\n\r\nprotocol ListProductsRoutable: AppRoutable { // Router\r\n    func showProduct(for id: Int)\r\n}</pre>\n<p>Here&#8217;s what happens when the controller loads via <code>viewDidLoad</code>: <em>interactor.fetchProducts &gt; presenter.presentFetchedProducts &gt; controller.displayFetchedProducts</em>. Do you see it yet? The cycle is there. Let&#8217;s take another look&#8230;</p>\n<p><em>Controller:</em></p>\n<pre class=\"lang:swift decode:true\">class ListProductsViewController: UIViewController {\r\n\r\n    private lazy var interactor: ListProductsBusinessLogic = ListProductsInteractor(\r\n        presenter: ListProductsPresenter(viewController: self),\r\n        productsWorker: ProductsWorker(store: ProductsMemoryStore())\r\n    )\r\n    \r\n    private lazy var router: ListProductsRoutable = ListProductsRouter(\r\n        viewController: self\r\n    )\r\n\r\n    private var viewModel: ListProductsModels.ViewModel?\r\n\r\n    override func viewDidLoad() {\r\n        super.viewDidLoad()\r\n\r\n        interactor.fetchProducts(\r\n            with: ListProductsModels.FetchRequest()\r\n        )\r\n    }\r\n}\r\n\r\nextension ListProductsViewController: ListProductsDisplayable {\r\n    \r\n    func displayFetchedProducts(with viewModel: ListProductsModels.ViewModel) {\r\n        self.viewModel = viewModel\r\n        tableView.reloadData()\r\n    }    \r\n\r\n    func display(error: AppModels.Error) {\r\n        let alertController = UIAlertController(\r\n            title: error.title,\r\n            message: error.message,\r\n            preferredStyle: .alert\r\n        )\r\n        \r\n        alertController.addAction(\r\n            UIAlertAction(title: \"OK\", style: .default, handler: nil)\r\n        )\r\n        \r\n        present(alertController, animated: true, completion: nil)\r\n    }\r\n}\r\n\r\nextension ListProductsViewController: UITableViewDelegate {\r\n    \r\n    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\r\n        guard let model = viewModel?.products[indexPath.row] else { return }\r\n        router.showProduct(for: model.id)\r\n    }\r\n}</pre>\n<p>The controller creates the instances to the interactor, injects the presenter, and also creates the router. When loaded, it creates a request model and calls the interactor:</p>\n<pre class=\"lang:swift decode:true\">struct ListProductsInteractor {\r\n    private let presenter: ListProductsPresentable\r\n    private let productsWorker: ProductsWorkerType\r\n    \r\n    init(presenter: ListProductsPresentable, productsWorker: ProductsWorkerType) {\r\n        self.presenter = presenter\r\n        self.productsWorker = productsWorker\r\n    }\r\n}\r\n\r\nextension ListProductsInteractor: ListProductsBusinessLogic {\r\n    \r\n    func fetchProducts(with request: ListProductsModels.FetchRequest) {\r\n        productsWorker.fetch {\r\n            guard let value = $0.value, $0.isSuccess else {\r\n                return self.presenter.presentFetchedProducts(error: $0.error ?? .unknownReason(nil))\r\n            }\r\n            \r\n            self.presenter.presentFetchedProducts(\r\n                for: ListProductsModels.Response(products: value)\r\n            )\r\n        }\r\n    }\r\n}\r\n</pre>\n<p>The <code>fetchProducts</code> function in the interactor calls the injected <code>productsWorker</code> (DI was left out for clarity, see my other post <a href=\"http://staging1.basememara.com/swift-protocol-oriented-dependency-injection/\" target=\"_blank\" rel=\"noopener\">Swifty Dependency Injection</a> for that topic). Underneath, it will call the appropriate storage, whether it be Core Data, Realm, or even a file system&#8230; the architecture doesn&#8217;t care and could be swapped out without affecting the cycle! When the data returns from the data storage asynchronously, it will wrap it in a response model and send it to the presenter:</p>\n<pre class=\"lang:swift decode:true \">struct ListProductsPresenter: ListProductsPresentable {\r\n    private weak var viewController: ListProductsDisplayable?\r\n    private let currencyFormatter: NumberFormatter\r\n    \r\n    init(viewController: ListProductsDisplayable?) {\r\n        self.viewController = viewController\r\n        self.currencyFormatter = NumberFormatter()\r\n        self.currencyFormatter.numberStyle = .currency\r\n    }\r\n}\r\n\r\nextension ListProductsPresenter {\r\n    \r\n    func presentFetchedProducts(for response: ListProductsModels.Response) {\r\n        let viewModel = ListProductsModels.ViewModel(\r\n            products: response.products.map {\r\n                ListProductsModels.ProductViewModel(\r\n                    id: $0.id,\r\n                    name: $0.name,\r\n                    content: $0.content,\r\n                    price: currencyFormatter.string(from: NSNumber(value: Float($0.priceCents) / 100)) ?? \"\\($0.priceCents / 100)\"\r\n                )\r\n            }\r\n        )\r\n        \r\n        viewController?.displayFetchedProducts(with: viewModel)\r\n    }\r\n    \r\n    func presentFetchedProducts(error: DataError) {\r\n        // Handle and parse error\r\n        let viewModel = AppModels.Error(\r\n            title: NSLocalizedString(\"products.error.title\", \"Title for product error\"),\r\n            message: String(format: NSLocalizedString(\"products.error.message\", \"Message for product error\"), error)\r\n        )\r\n        \r\n        viewController?.display(error: viewModel)\r\n    }\r\n}</pre>\n<p>The presenter will convert the response into a view model, applying any formatting and localization, and call the controller function to display it. The view model properties are almost always strings because the view will just display. If the interactor gave the presenter an error, it will even create a view model out of the error and let the controller display it.</p>\n<p>The models are encapsulated in an enum and are only relevant to its own use case. You do not cross boundaries without being wrapped in one of the models:</p>\n<pre class=\"lang:swift decode:true \">enum ListProductsModels {\r\n    \r\n    struct FetchRequest {\r\n        \r\n    }\r\n    \r\n    struct SearchRequest {\r\n        let text: String\r\n    }\r\n    \r\n    struct Response {\r\n        let products: [ProductType]\r\n    }\r\n    \r\n    struct ViewModel {\r\n        let products: [ProductViewModel]\r\n    }\r\n    \r\n    struct ProductViewModel {\r\n        let id: Int\r\n        let name: String\r\n        let content: String\r\n        let price: String\r\n    }\r\n}</pre>\n<p>Finally the router in case the controller has to send the user to another use case:</p>\n<pre class=\"lang:swift decode:true\">struct ListProductsRouter {\r\n    weak var viewController: UIViewController?\r\n    \r\n    init(viewController: UIViewController?) {\r\n        self.viewController = viewController\r\n    }\r\n}\r\n\r\nextension ListProductsRouter: ListProductsRoutable {\r\n    \r\n    func showProduct(for id: Int) {\r\n        let storyboard = UIStoryboard(name: \"ShowProduct\", bundle: nil)\r\n        \r\n        guard let controller = storyboard.instantiateInitialViewController()) as? ShowProductViewController\r\n            else { return assertionFailure(\"Invalid controller for storyboard \\(storyboard).\") }\r\n\r\n        controller.productID = id\r\n\r\n        viewController?.present(controller, animated: true)\r\n    }\r\n}</pre>\n<p>This way, the the controller is decoupled from the routing decisions and can be used from the controller as: <code>router.showProduct(for: productID)</code>.</p>\n<h2>Conclusion</h2>\n<p>The Clean Architecture is flexible, scalable, and maintainable. Although it is more verbose than other architecture designs, it is necessary so components are not tightly coupled. That means everything must reference each other by its protocol, not its concrete types. And when crossing boundaries, data must be wrapped in request / response models, or again components will be coupled. In a future post, I will discuss how to unit test the whole stack.</p>\n<p>See a <a href=\"https://github.com/iosmentor-io/Swift-Clean-Architecture\" target=\"_blank\" rel=\"noopener\">working example of this Clean Architecture</a> to try it out for yourself.</p>\n<p><em><strong>HAPPY CODING!!</strong></em></p>\n<hr />\r\n<h2 style=\"text-align: center;\">FREE Masterclass Webinar: Creating Cross-Platform Swift Frameworks for iOS, watchOS, &amp; tvOS!</h2>\r\n<p>An essential part of creating scalable apps is code organization. Watch my updated \"Creating Cross-Platform Swift Frameworks\" webinar; it will change the way you build iOS apps! <a href=\"https://iosmentor.io/webinars/swift-frameworks/\" target=\"_blank\" rel=\"noopener\"><img src=\"http://staging1.basememara.com/wp-content/uploads/2018/04/swift-framework-webinar.png\" alt=\"\" width=\"406\" height=\"257\" class=\"aligncenter size-full wp-image-41351\" /></a></p>\n")
        XCTAssertEqual(model.commentCount, 10)
        XCTAssertEqual(model.link, "https://staging1.basememara.com/swift-clean-architecture/")
        XCTAssertEqual(model.authorID, 2)
        XCTAssertEqual(model.mediaID, 41346)
        XCTAssertEqual(model.meta["_series_part"], "1")
        XCTAssertEqual(model.terms, [80, 79, 53, 14, 62, 50, 55])
        XCTAssertEqual(model.createdAt, DateFormatter.iso8601.date(from: "2018-04-22T22:03:20"))
        XCTAssertEqual(model.modifiedAt, DateFormatter.iso8601.date(from: "2018-09-30T11:47:51"))
    }
}

extension PostModelTests {
    
    func testNullDecoding() throws {
        let model = try JSONDecoder.default.decode(Post.self, fromJSON: #fileID, suffix: "2")
            
        XCTAssertEqual(model.id, 41294)
        XCTAssertEqual(model.title, "So Swift, So Clean Architecture for iOS")
        XCTAssertEqual(model.slug, "swift-clean-architecture")
        XCTAssertEqual(model.type, "post")
        XCTAssertEqual(model.excerpt, "The topic of iOS app architecture has evolved a long way from MVC. Unfortunately, the conversation becomes a frameworks and patterns war. The reality is: Rx is a framework; MVVM is a presentation pattern; and so on. Frameworks and patterns always come and go, but architectures are timeless. In this post, we will examine the Clean Architecture for building scalable apps in iOS.")
        XCTAssertEqual(model.content, "This is the content.")
        XCTAssertEqual(model.commentCount, 10)
        XCTAssertEqual(model.link, "https://staging1.basememara.com/swift-clean-architecture/")
        XCTAssertEqual(model.authorID, 2)
        XCTAssertNil(model.mediaID)
        XCTAssertEqual(model.terms, [80, 79, 53, 14, 62, 50, 55])
        XCTAssertEqual(model.createdAt, DateFormatter.iso8601.date(from: "2018-04-22T22:03:20"))
        XCTAssertEqual(model.modifiedAt, DateFormatter.iso8601.date(from: "2018-09-30T11:47:51"))
    }
}
#endif
