{"id":23261527,"url":"https://github.com/theinkedengineer/bloodymary","last_synced_at":"2025-08-20T18:31:19.528Z","repository":{"id":56903829,"uuid":"220779446","full_name":"TheInkedEngineer/BloodyMary","owner":"TheInkedEngineer","description":"A one-directional MVVM framework.","archived":false,"fork":false,"pushed_at":"2020-05-06T07:16:59.000Z","size":1881,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2024-12-15T06:18:34.948Z","etag":null,"topics":["apple","custom-navigation","ios","mvvm","navigation","swift","viewmodel"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheInkedEngineer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-10T11:21:15.000Z","updated_at":"2023-07-16T20:30:09.000Z","dependencies_parsed_at":"2022-08-21T02:50:20.567Z","dependency_job_id":null,"html_url":"https://github.com/TheInkedEngineer/BloodyMary","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheInkedEngineer%2FBloodyMary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheInkedEngineer%2FBloodyMary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheInkedEngineer%2FBloodyMary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheInkedEngineer%2FBloodyMary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheInkedEngineer","download_url":"https://codeload.github.com/TheInkedEngineer/BloodyMary/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230441613,"owners_count":18226406,"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":["apple","custom-navigation","ios","mvvm","navigation","swift","viewmodel"],"created_at":"2024-12-19T13:33:15.457Z","updated_at":"2024-12-19T13:33:15.931Z","avatar_url":"https://github.com/TheInkedEngineer.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"logo.png\" alt=\"BloodyMary logo\" width=\"400\"\u003e\n\u003c/p\u003e\n\n[![Twitter](https://img.shields.io/twitter/url/https/theinkedgineer.svg?label=TheInkedgineer\u0026style=social)](https://twitter.com/theinkedgineer)\n\n# BloodyMary\n\n`BloodyMary` is a stripped down version of [Tempura](https://github.com/BendingSpoons/tempura-swift). It is a one-directional [MVVM](https://en.wikipedia.org/wiki/Model–view–viewmodel) inspired framework that helps you write clean code and seperate the responsabilities properly.\n\n`BloodyMary` has its own custom navigation system layer that works on top on Apple's native navigation system so none of the Apple magic is lost. `BloodyMary`'s navigation system can work next to Apple's way with no problem what so ever.\n\n`BloodyMary`'s  navigation system aims to help you write the least code needed to get the most out of your app. You provide the SDK with the routes identifiers and their respective view controllers, and BloodyMary does the rest. From instantiating the correct VC to properly pushing it or presenting it etc..\nThe `show` and `hide` functions are done on a single queue in a synchronous way.\n\nThe `1.3.x` is the version containing the navigation system, currently being used on different `WiseEmotions` libraries and `Telepass` iOS app.\n\n### RoadMap\n\n- [x] Custom Navigation System\n- [ ] Integrate Combine\n\n# 1. Requirements and Compatibility\n\n| Swift               | BloodyMary     |  iOS     |\n|-----------------|----------------|---------|\n|       5.1+          | 1.0.x               |  10+     |\n|       5.1+          | 1.1.x                |  10+     |\n|       5.1+          | 1.2.x                |  10+     |\n|       5.1+          | 1.3.x                |  10+     |\n\n# 2. Installation\n\n## Cocoapods\n\nAdd the following line to your Podfile\n` pod 'BloodyMary' ~\u003e '1.3.1' `\n\n\n# 3. Documentation\n\n`BloodyMary` is fully documented. Checkout the documentation [**here**](https://theinkedengineer.github.io/BloodyMary/docs/1.1.x/index.html).\n\n# 4. Code Example\n## The one directional MVVM\n\n```swift\nstruct RemindersListViewModel: BMViewModel {\n  var reminders: [Models.Reminders]\n  \n  func shouReloadData(oldModel model: RemindersListViewModel) -\u003e Bool {\n    model.reminders.count != self.reminders.count\n  }\n}\n\nclass RemindersListView: BMViewWithViewControllerAndViewModel {\n  [...]\n  \n  func update(oldModel: RemindersListViewModel?) {\n    guard let model = self.viewModel else { return }\n    if model.shouReloadData(oldModel: oldModel) {\n      self.tableView.reloadData()\n    }\n  }\n  \n  [...]\n}\n\nclass RemindersListViewController: BMViewController {\n  func setupInteractions() {\n    self.rootView.didTapDeleteButton = { [weak self] index in\n      guard let self = self, let model = self.viewModel else { return }\n      let newReminders = model.reminders.remove(at: index)\n      let newViewModel = RemindersListViewModel(reminders: newReminders)\n      self.update(to: newViewModel)\n    }\n  }\n}\n```\n\n## The Navigation System\n\n### Providing the configuration for the router\n\n```swift\nimport BloodyMary\n\nstruct DependenciesContainer: RoutingConfigurationProvider {\n  // prevent it from being instantiated.\n  private init() {}\n  \n  static var shared = DependenciesContainer()\n  \n  var screensAndDestinations: [ScreenIdentifier : RoutableViewController.Type] {\n    [\n      \"green\": GreenViewController.self,\n      \"red\": RedViewController.self\n    ]\n  }\n  \n  lazy var router: Router = Router(with: self)\n}\n\n```\n\n### Installing the root\n\n```swift\nimport UIKit\nimport BloodyMary\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  \n  var window: UIWindow?\n  \n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n    \n    self.window = self.window ?? UIWindow(frame: UIScreen.main.bounds)\n    \n    guard let window = self.window else {\n      return false\n    }\n    \n    let redVC = RedViewController()\n    let redVM = RedViewModel()\n    redVC.viewModel = whiteVM\n\n    DependenciesContainer.shared.router.installRoot(using: greenViewController, in: window)\n\n    return true\n  }\n}\n```\n\n### Navigating from a view to another\n\n```swift\nclass RedViewController: BMViewController\u003cRedView\u003e, Routable {\n  \n  func assign(model: Any) -\u003e Bool {\n    guard let model = model as? RedViewModel else {\n      return false\n    }\n    self.viewModel = model\n    return true\n  }\n  \n  static var screenIdentifier: ScreenIdentifier {\n    \"red\"\n  }\n  \n  override func setupInteractions() {\n    super.setupInteractions()\n    \n    self.rootView.didTapButton = { _ in\n      let greenRoutableObject = RoutableObject(\n        screenIdentifier: GreenViewController.screenIdentifier,\n        viewModel: GreenViewModel(),\n        navigationStyle: .modal(),\n        animated: false\n      )\n      \n      DependenciesContainer.shared.router.show(\n        routableElements: greenRoutableObject, completion: nil)\n    }\n  }\n}\n```\n\n# 5. Contribution\n\n**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)\n\n## Generate the project\n\nTo generate this project locally, you need [xcodegen](https://github.com/yonaskolb/XcodeGen). It is a great tool to customize a project and generate it on the go.\n\nYou can either install it manually following their steps, or just run my `setup.sh` script. It automatically installs [Homebrew](https://brew.sh) if it is missing, installs `xcodegen`, removes existing (if present) `.xcodeproj`, run `xcodegen` and moves configuratiom files to their appropriate place.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheinkedengineer%2Fbloodymary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheinkedengineer%2Fbloodymary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheinkedengineer%2Fbloodymary/lists"}