{"id":7545370,"url":"https://github.com/vladimir-kaltyrin/exchanger","last_synced_at":"2025-05-11T01:30:53.124Z","repository":{"id":232100754,"uuid":"96760154","full_name":"vladimir-kaltyrin/exchanger","owner":"vladimir-kaltyrin","description":"Example of implementing VIPER 💎 and Clean Architecture 💍 in Objective-C ","archived":false,"fork":false,"pushed_at":"2017-12-21T16:25:54.000Z","size":12405,"stargazers_count":13,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-17T04:35:45.143Z","etag":null,"topics":["abstract-factory-pattern","clean-architecture","coredata","currency-exchange-rates","exchange-rates","objc","objective-c","soa","unit-testing","viper","viper-architecture","viper-modules","viper-pattern-architecture"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vladimir-kaltyrin.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-10T09:30:34.000Z","updated_at":"2024-03-29T09:16:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"0e6c83bb-61ed-4890-bc60-8ddf1913a781","html_url":"https://github.com/vladimir-kaltyrin/exchanger","commit_stats":null,"previous_names":["vladimir-kaltyrin/exchanger"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-kaltyrin%2Fexchanger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-kaltyrin%2Fexchanger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-kaltyrin%2Fexchanger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-kaltyrin%2Fexchanger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vladimir-kaltyrin","download_url":"https://codeload.github.com/vladimir-kaltyrin/exchanger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253504438,"owners_count":21918803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abstract-factory-pattern","clean-architecture","coredata","currency-exchange-rates","exchange-rates","objc","objective-c","soa","unit-testing","viper","viper-architecture","viper-modules","viper-pattern-architecture"],"created_at":"2024-04-08T00:39:16.126Z","updated_at":"2025-05-11T01:30:53.118Z","avatar_url":"https://github.com/vladimir-kaltyrin.png","language":"Objective-C","funding_links":[],"categories":["Misc"],"sub_categories":["VIPER"],"readme":"[![Build Status](https://www.bitrise.io/app/db9b8a614ca81158/status.svg?token=pnK66giJ4HQm8cRamwvSvQ\u0026branch=develop)](https://www.bitrise.io/app/db9b8a614ca81158)\n\n![Exchanger](https://github.com/vkaltyrin/exchanger/blob/develop/exchanger.png)\n\n# Exchanger\n\nThe Exchanger is a simple iOS application demonstrating one of approaches to implement VIPER 💎 architecture in modern Objective-C.\n\n# Table of contents\n\n* [About](#about)\n* [Setup](#setup)\n    * [CocoaPods](#cocoapods)\n    * [OCLint](#oclint)\n    * [XCPretty](#xcpretty)\n* [Notes on implementation](#notes)\n    * [Architecture](#architecture)\n    * [VIPER](#viper)\n    * [Type Inference](#typeinference)\n    * [Blocks](#blocks)\n    * [CarouselView](#carouselview)\n    * [Persistance](#persistance)\n    * [Tests](#tests)\n\n\u003ca name=\"about\"/\u003e\n\n## About\n\nThe application is a fairly straightforward currency converter. It takes a reference rate from a European Central Bank by parsing its [public XML](http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml) and provides a feature to exchange any currency including EUR, USD and GBP in any combination. The exchange rate is automatically updated each 30 seconds.\n\nWhen app starts there is a limited balance with 100 units of each currency.\nThere are two text inputs on the screen, both are cyclic carousel views for choosing currency to exchange.\n\nYouTube video of how it works:\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=SdipG8ApWWc\n\" target=\"_blank\"\u003e\u003cimg src=\"http://img.youtube.com/vi/SdipG8ApWWc/0.jpg\"\nalt=\"Exchanger app video\" width=\"480\" height=\"360\" border=\"10\" /\u003e\u003c/a\u003e\n\nThe app core is carefully designed with love ❤️ to SOLID in pure Objective-C using VIPER architecture combined with SOA. Meanwhile, unit tests are written in Swift.\n\nIf you have any questions just [email me](mailto:vkasci@gmail.com). Feel free to open issues 😀\n\n\u003ca name=\"setup\"/\u003e\n\n## Setup\n\n\u003ca name=\"cocoapods\"/\u003e\n\n### CocoaPods\n\nTo install all project dependencies just use CocoaPods:\n\n```bash\npod install\n```\n\n\u003ca name=\"oclint\"/\u003e\n\n### OCLint\n\nIf OCLint is not installed on your machine then run following commands in Terminal:\n\n```bash\nbrew tap oclint/formulae\nbrew install oclint\n```\n\n\u003ca name=\"xcpretty\"/\u003e\n\n### XCPretty\n\nIf XCPretty is not installed on your machine then run following commands in Terminal:\n\n```bash\ngem install xcpretty\n```\n\n\u003ca name=\"notes\"/\u003e\n\n## Notes on implementation\n\n### Architecture\n\n\u003ca name=\"architecture\"/\u003e\n\nThe app is intended to implement the clean architecture.\n\n![Clean architecture](https://github.com/vkaltyrin/exchanger/blob/develop/architecture.png)\n\n\u003ca name=\"viper\"/\u003e\n\n### VIPER\n\nEach screen is represented as VIPER module. In this implementation of VIPER there is a Router\nclass for navigation between screens and functional callbacks to interact with module, for example:\n\n```objective-c\n\n@protocol ExchangeMoneyModule \u003cNSObject\u003e\n@property (nonatomic, strong) void(^onFinish)();\n\n- (void)dismissModule;\n\n@end\n\n```\n\n\u003ca name=\"typeinference\"/\u003e\n\n### Type Inference\n\nType Inference is a common feature in Swift, but Objective-C by default doesn't provide it. It's easy to avoid this\nissue by using C macroses, as it's provided below:\n\n```objective-c\n#define let __auto_type const\n#define var __auto_type\n```\n\nWithout using __auto_type:\n```objective-c\nNSArray\u003cCurrency *\u003e *currencies = data.currencies;\n```\n\nWith using using __auto_type:\n```objective-c\nlet currencies = data.currencies;\n```\n\n\u003ca name=\"blocks\"/\u003e\n\n### Blocks\n\nThere is a common pattern in Objective-C to call a block:\n\n```objective-c\nif (block != nil) {\n    block();\n}\n```\n\nWith optionals and closures syntax introduced in Swift this syntax looks especially overweighted.\nThere is a C macros to deal with that:\n\n```objective-c\n#define safeBlock(block, ...) if (block != nil) { block(__VA_ARGS__); }\n```\n\n\u003ca name=\"carouselview\"/\u003e\n\n### CarouselView\n\nCarouselView implementation uses dummy UITextField in order to keep some first responder on the screen.\nIt that way the keyboard is always on the screen which is a nice UX.\n\n\u003ca name=\"persistance\"/\u003e\n\n### Persistance\n\nApp saves the state using simple Core Data storage.\n\n\u003ca name=\"Tests\"/\u003e\n\n### Tests\n\nUnit tests are provided for service layer and core layer including formatters. Some folks prefer to write unit tests for Presenter and Interactor. In practice it may be a case of accidental complexity. When business logic is located mainly in services, then unit tests for service layer are appropriate. Interactor just passes values from presenter to services. Presenter's code is often changing and it makes more sense to write UI tests. In this way unit tests for Interactor and Presenter are recommended but there is no strict need to write tests for them meanwhile code keeps to be testable and maintable.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladimir-kaltyrin%2Fexchanger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvladimir-kaltyrin%2Fexchanger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladimir-kaltyrin%2Fexchanger/lists"}