{"id":13694173,"url":"https://github.com/kudoleh/iOS-Modular-Architecture","last_synced_at":"2025-05-03T01:31:36.218Z","repository":{"id":42065592,"uuid":"224473419","full_name":"kudoleh/iOS-Modular-Architecture","owner":"kudoleh","description":"Template iOS application using Modular Architecture","archived":false,"fork":false,"pushed_at":"2023-05-06T10:32:52.000Z","size":13080,"stargazers_count":747,"open_issues_count":0,"forks_count":127,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-04-04T07:36:24.886Z","etag":null,"topics":["clean-architecture","cocoapods","ios-app","ios-swift","modular-architecture","modularization"],"latest_commit_sha":null,"homepage":"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kudoleh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2019-11-27T16:32:38.000Z","updated_at":"2025-03-31T09:26:11.000Z","dependencies_parsed_at":"2024-04-12T23:19:49.225Z","dependency_job_id":"dca54419-1846-46cc-8933-76ff902c022e","html_url":"https://github.com/kudoleh/iOS-Modular-Architecture","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kudoleh%2FiOS-Modular-Architecture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kudoleh%2FiOS-Modular-Architecture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kudoleh%2FiOS-Modular-Architecture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kudoleh%2FiOS-Modular-Architecture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kudoleh","download_url":"https://codeload.github.com/kudoleh/iOS-Modular-Architecture/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252130407,"owners_count":21699085,"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":["clean-architecture","cocoapods","ios-app","ios-swift","modular-architecture","modularization"],"created_at":"2024-08-02T17:01:25.954Z","updated_at":"2025-05-03T01:31:33.389Z","avatar_url":"https://github.com/kudoleh.png","language":"Swift","funding_links":[],"categories":["Sample"],"sub_categories":["RSS"],"readme":"# Modular Architecture on iOS\n\niOS Project implemented with Modular Architecture. (Can be used as Template project by replacing item name “Movie”). **More information in medium post**: \u003ca href=\"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9\"\u003eMedium Post about Modular Architecture\u003c/a\u003e\n\n![Alt text](README_FILES/ModulesDependencies.png?raw=true \"Modules Dependencies\")\n*More information in medium post*: \u003ca href=\"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9\"\u003eMedium Post \u003c/a\u003e\n\n# Including Authentication module (initial scaling)\n\n\u003ca href=\"http://www.google.com\"\u003e\n\u003cimg src=\"README_FILES/ModulesDependenciesAuth.jpg\" alt=\"Modules Dependencies with Authentication\" width=\"650\"/\u003e\n\u003c/a\u003e\n\n*More information in medium post*: \u003ca href=\"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9\"\u003eMedium Post \u003c/a\u003e\n\n# How it can scale:\n\n\u003ca href=\"http://medium.com\"\u003e\n\u003cimg src=\"README_FILES/ModulesScaled.jpeg\" alt=\"Open medium to read about\"/\u003e\n\u003c/a\u003e\n\n**Check medium post for more information**: \u003ca href=\"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9\"\u003eMedium Post \u003c/a\u003e\n\n## Video 1: Extracting Networking Service Module. \u003ca href=\"#steps-to-create-module\"\u003esee steps\u003c/a\u003e\n\u003ca href=\"https://www.youtube.com/embed/e2D-omTFJSA\"\u003e\n\u003cimg src=\"README_FILES/VideoPart1Preview.png\" alt=\"Play video\" width=\"650\"/\u003e\n\u003c/a\u003e\n\n## Video 2: Extracting Movies Search Feature Module. \u003ca href=\"#steps-to-create-module\"\u003esee steps \u003c/a\u003e\n\n\u003ca href=\"https://www.youtube.com/embed/m1lu_23iV78\"\u003e\n\u003cimg src=\"README_FILES/VideoPart2Preview.png\" alt=\"Play video\" width=\"650\"/\u003e\n\u003c/a\u003e\n\n## Requirements: \n* **Xcode Version 11.2.1+  Swift 5.0+ CocoaPods 1.8.4+**\n\n# Steps to create module\n* **Step 1. Setup App project with CocoaPods: (Video 1 00:06)**\n\t* Open Terminal and change directory `cd` to folder with your project App.xcodeproj file, and execute following commands:\n\t\t* `pod init` \n    \t* inside Podfile \n    \t\t* uncomment `platform :ios, '9.0'`,  and change `'9.0'` to `14.0` or `15.0`\n    \t\t* add line: `workspace 'AppName.xcworkspace'`\n    \t\t* add line: `project 'AppName.xcodeproj'`\n    \t* `pod install` \n* **Step 2. Create new Module with CocoaPods: (Video 1 00:48)**\n\t* In the same folder from previous step create a new folder with name `DevPods`: \n\t\t* `mkdir DevPods` \n\t* Create New Module inside `DevPods` folder:\n\t\t* `cd DevPods`\n\t\t* `pod lib create ModuleName`\n\t\t* with Platform: `iOS`; Language: `Swift`; Include a Demo App: `Yes`; Test framework and view based testing we can skip\n\n\t\tNote: To use your own pod-template you can add the parameter `--template-url=URL` where URL is the git repo containing a compatible template.\n* **Step 3. The Module's Example Demo project will be opened automatically, we need to update iOS and Swift versions: (Video 1 01:27)**\n\t* Select example project and set `In Deployment Info` to `iOS 14.0` or `iOS 15.0`\n\t* In Build Settings search for `Swift` and set Swift Language Version to `Swift 5`. \n\t* Remove Test Target and Test folder inside the project\n\t* Close this demo example project (otherwise you will not be able to open it from App.xcworkspace)\n* **Step 4. Cleaning up `DevPods/ModuleName` folder: (Video 1 01:48)** \n\t * Show hidden files with `Cmd + Shift + .` and delete files: `.git`, `.gitignore` and `.travis.yamls`\n\t* Delete files: `_Pod.xcodeproj`, `Example/Podfile`, `Example/Podfile.lock`, `Example/Pods`, `Example/ModuleName.xcworkspace`\n* **Step 5. Edit Podfile of project located in same folder as app's project App.xcworkspace file: (Video 1 02:10)** \n\n\t* Add `module_name_pod` inside Podfile:\n\t\n\t\t```ruby\n\t\tdef module_name_pod\n\t\t    pod 'ModuleName', :path =\u003e 'DevPods/ModuleName'\n\t\tend\n\t\t```\n\t* Move nested test targets outside target 'App':\n\t\n\t\t```ruby\n\t\ttarget 'App' do\n\t\t...\n\t\tend\n\n\t\ttarget 'AppTests' do\n\t\t\tinherit! :search_paths\n\t\tend\n\t\t\n\t\ttarget 'AppUITests' do\n\t\tend\n\t\t```\n\t* Add module's example target so we can develop this module from same main App's workspace:\n\n\t\t```ruby\n\t\ttarget 'ModuleName_Example' do\n\t\t\tuse_frameworks!\n\t\t\tproject 'DevPods/ModuleName/Example/ModuleName.xcodeproj'\n\t\t\t\n\t\t\tmodule_name_pod\n\t\tend\n\t\t```\n* **Step 6. Move files from main App into Module's Pod: (Video 1 03:14)**\n\t* Inside `DevPods/ModuleName/ModuleName` folder remove `Assets` and `Classes` folders and create folder with name `Module`. \n\t* Move from main App to the `Module` folder all module's files (e.g. .swift, .xcassets, .storyboard, .xcdatamodeld...). After moving them from main App do not forget to delete them also from main App's target. For moving files is better to not use Xcode, and do it manually or with terminal.\n        * Edit ModuleName.podspec:\n\t\t\t* Set ios deployment target: `s.ios.deployment_target = '14.0'` or `15.0`\n\n            * Replace `s.source_files = ...`  to: **(Video 1 03:53)**\n\n\t            ```ruby\n\t            s.source_files = 'MoviesSearch/Module/**/*.{swift}'\n\t            s.resources = \"MoviesSearch/Module/**/*.{xcassets,json,storyboard,xib,xcdatamodeld}\"\n\t            ```\n\t            **Note**: if you have Core Data model with `xcdatamodel` extension you will need to convert it into `xcdatamodeld` by selecting this file and `Editor or Help -\u003e Add Model Version..`\n\t            \n\t            **Note**: when you create new Group inside `Development Pods/ModuleName` with Xcode, it places its folder in wrong place outside of `Module` (pod folder). And it needs to be moved manually into `Module` folder using Finder, and then `pod install`\n\n* **Step 7. Add all internal or external dependencies for this module. Add them inside `ModuleName.podspec` file located inside `DevPods/ModuleName` folder: (Video 2 03:33)**\n\n        # internal dependency (module you have created before)\n        s.dependency 'ModuleName2' \n\n        # external dependency 3rd party framework \n        s.dependency 'PromiseKit'\n**Note**: If you have not yet extracted your dependency and you need it here you can delegate this functionality to main App. Just create interface using protocol and implement it inside \nyour main App. For example if you need to open chat from your module, then create protocol interface inside your module with function openChat(forUserId:inView:) and then implement it inside main App, and it will be injected from main App into DIContainer of the module.\nAlso closures or delegation can be used. \n\n* **Step 8. After `pod install` and compiling main App we will see some errors that we have to fix: (Video 2 03:45)**\n\t* Inside main app, add `import ModuleName` in all files where it is needed\n    * Change to public all types or functions that needs to be accessed from outside of the module, from main app or other module\n    * Change all resources initialisers to initialise them with module's bundle (e.g. images, storyboards, nibs or CoreData models xcdatamodeld): **(Video 2 05:15)**\n    \n        ```swift\n        // Image load from bundle\n        UIImage(named: \"image_name\", in: Bundle(for: Self.self), compatibleWith: nil)\n        // Image Literal\n        final class LiteralBundleImage: _ExpressibleByImageLiteral {\n    \t\tlet image: UIImage?\n    \t\trequired init(imageLiteralResourceName name: String) {\n        \t\timage = UIImage(named: name, in: Bundle(for: Self.self), compatibleWith: nil)\n    \t\t}\n\t\t}\n\t\tlet image = (#imageLiteral(resourceName: \"image_name\") as LiteralBundleImage)\n        \n        // Storyboards and Nibs load from bundle\n        UIStoryboard(name: \"name\", bundle: Bundle(for: Self.self))\n        UINib(nibName: \"name\", bundle: Bundle(for: Self.self))\n        \n        // Core Data model load from bundle\n        guard let modelURL = Bundle(for: Self.self).url(forResource: \"ModelFileName\", withExtension: \"momd\"),\n              let mom = NSManagedObjectModel(contentsOf: modelURL)\n              else {\n                    fatalError(\"Unable to located Core Data model\")\n        }\n        let container = NSPersistentContainer(name: \"Name\", managedObjectModel: mom)\n    ```\n        Note: the file must have .xcdatamodeld extension, to convert it from xcdatamodel extension just Add Model Version..\n        \n* **Step 9. Move Module's tests from main App into module's Pod: (Video 2 07:20)**\n    * Crete folder with name `Tests` inside  `DevPods/ModuleName/ModuleName/`\n    * Move all files with tests from main App folder to `DevPods/ModuleName/ModuleName/Tests`.  **Note**:  Move them manually or using terminal, avoid doing it directly with Xcode groups.\n    * Add to ModuleName.podspec:\n    \n        ```ruby\n        s.test_spec 'Tests' do |test_spec|\n            test_spec.source_files = 'ModuleName/Tests/**/*.{swift}'\n        end\n        ```\n\n    * Add to main App's Podfile module's pod `, :testspecs =\u003e ['Tests']`\n\n        ```ruby\n        def module_name_pod\n        \tpod 'ModuleName', :path =\u003e 'DevPods/ModuleName', :testspecs =\u003e ['Tests']\n        end\n        ```\n        * Add to all unit tests files imports: `@testable import ModuleName`\n        * Run in terminal `pod install` for main App Podfile\n        * To make our Module's unit test app to run unit test with `Cmd + U` from main App schema:\n        * Edit schema `ModuleName-Example`, inside Test section, add unit tests from Pod: `under Pod -\u003e ModuleName-Unit-Tests`\n* **Step 10. Fix ModuleName-Example schema used for developing module in isolation: (Video 2 09:45)**\n    * Switch from main App schema to ModuleName-Example schema and try to build\n    * **Note**: AppDelegate method will have some warning and error that we have to fix, you can just copy paste it form main App\n* **Step 11. To make our Module's Example app to run unit test with `Cmd + U`, we follow the same step as we did for main App: (Video 2 10:10)**\n    * Edit schema `ModuleName-Example`, inside Test section, add unit tests from Pod: `under Pod -\u003e ModuleName-Unit-Tests`\n    \n# Module as Static Library [Optional]: \n\n* Change your modules to be as Static Libraries (We made it as \u003ca href=\"https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html\"\u003eDynamic Library\u003c/a\u003e in previous steps):\n\t * Remove from main App's Podfile: `use_frameworks!`\n    * Replace `s.resources` with `s.resource_bundles` in all modules' .podspec files (ModuleName.podspec):\n    \u003cbr\u003e**Note**: `resource_bundles` has different structure: `'ModuleName' =\u003e [ ... ]`, where `'ModuleName'` is bundle name:\n    \n        ```ruby\n        s.resources = 'ModuleName/Module/**/*.{xcassets,json,storyboard,xib,xcdatamodeld}'\n        ```\n        \n        Replace `s.resources` with: \n        \n        ```ruby\n        s.resource_bundles = {\n   \t\t\t'ModuleName' =\u003e ['ModuleName/Module/**/*.{xcassets,json,storyboard,xib,xcdatamodeld}']\n  \t\t}\n        ```\n\n    * Replace all `Bundle(for: Self.self)` appearances with call to extension: `Bundle(for: Self.self).resource`\n\n        ```swift\n         extension Bundle {\n\t\t     var resource: Bundle {\n\t\t        return Bundle(url: resourceURL!.appendingPathComponent(\"ModuleName.bundle\"))!\n\t\t     }\n\t\t }\n        ```\n\t* If you have CoreData inside a module then open .xcdatamodeld file and change `Codegen` to `Manual/None` and `Create NSManagedObject Subclass..` for all entities\n\t\n# Module's Testing in CI/CD [Optional]: \n\n* To keep always our modules buildable we need to build and run tests for each module on our Pipeline in CI. As example we can use [Fastlane](https://docs.fastlane.tools/actions/scan/) and [Travis CI](https://travis-ci.org/):\n\t\n\t* [Fastlane script](fastlane/Fastfile)\n\n\t\t```ruby\n\t\tlane :test do |options|\n\t\t\n\t\t  # Check if all modules are buildable\n\t\t  all_modules_schemes.each do |s|\n\t\t    UI.message \"Testing if module #{s} is buildable\"\n\t\t    scan(\n\t\t      scheme: s,\n\t\t      device: simulator,\n\t\t      build_for_testing: true,\n\t\t    )\n\t\t  end\n\t\t\t\n\t\t  # Run all unit and UI tests, and test if App is buildable\n\t\t  scan(\n\t\t    scheme: \"App\",\n\t\t    device: simulator,\n\t\t  )\n\t\t\t\n\t\tend\n\t\t```\n\t* [Travis CI script](.travis.yml)\t\n\t\t  \n\t\t```ruby\n\t\tos: osx\n\t\tosx_image: xcode11.2\n\t\tlanguage: swift\n\t\tscript:\n\t\t- fastlane test\n\t\t```\n\n**Check medium post for more information**: \u003ca href=\"https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9\"\u003eMedium Post \u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkudoleh%2FiOS-Modular-Architecture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkudoleh%2FiOS-Modular-Architecture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkudoleh%2FiOS-Modular-Architecture/lists"}