{"id":21149443,"url":"https://github.com/xemantic/github-users","last_synced_at":"2025-07-09T09:31:08.360Z","repository":{"id":53301709,"uuid":"91070819","full_name":"xemantic/github-users","owner":"xemantic","description":"Lists GitHub users. Minimal app demonstrating cross-platform app development (Web, Android, iOS) where core logic is shared and transpiled from Java to JavaScript and Objective-C.","archived":false,"fork":false,"pushed_at":"2021-03-31T18:51:25.000Z","size":183,"stargazers_count":6,"open_issues_count":6,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-14T12:47:13.580Z","etag":null,"topics":["android","cross-compilation","cross-platform","eventbus","frontend","gwt","ios","j2objc","java","javascript","objective-c","reactive-programming","rxjava","transpilation","web"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xemantic.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}},"created_at":"2017-05-12T08:46:30.000Z","updated_at":"2020-10-28T18:56:27.000Z","dependencies_parsed_at":"2022-09-02T02:00:29.636Z","dependency_job_id":null,"html_url":"https://github.com/xemantic/github-users","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/xemantic%2Fgithub-users","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fgithub-users/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fgithub-users/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fgithub-users/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xemantic","download_url":"https://codeload.github.com/xemantic/github-users/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225521914,"owners_count":17483515,"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":["android","cross-compilation","cross-platform","eventbus","frontend","gwt","ios","j2objc","java","javascript","objective-c","reactive-programming","rxjava","transpilation","web"],"created_at":"2024-11-20T09:35:58.508Z","updated_at":"2024-11-20T09:35:59.024Z","avatar_url":"https://github.com/xemantic.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://api.travis-ci.org/xemantic/github-users.svg?branch=master)](https://travis-ci.org/xemantic/github-users)\n\n# About github-users\n\nLists GitHub users. Minimal app demonstrating cross-platform app development\n(Web, Android, iOS) where core logic is shared and\n[transpiled](https://en.wikipedia.org/wiki/Source-to-source_compiler)\nfrom Java to JavaScript and Objective-C.\n\n# Demo\n\nThis project provides only source code of the shared logic. Here is\nthe web version:\n\nhttps://github-users-web.appspot.com/\n\nSee also: \n\n* [github-users-web](https://github.com/xemantic/github-users-web)\n* [github-users-android](https://github.com/xemantic/github-users-android)\n* [github-users-ios](https://github.com/xemantic/github-users-ios)\n\n# Why I started such a project?\n\nGoogle claims that applications like [Google Inbox](https://www.google.com/inbox/)\ncan [share up to 70% of client code between all the platforms](https://gmail.googleblog.com/2014/11/going-under-hood-of-inbox.html).\nIt's significant achievement when taking into account:\n\n* reduced Total Cost of Ownership with only one code base to maintain\n* naturally synchronized development process across all the teams\n(backend, presentation logic, frontend, mobile)\n* the same features released for all the platforms\n* and the time-to-market for new value is shorter\n\nJust to name a few. Usually cross-platform development tools fall into these categories:\n\n* abstraction over native UI components and IO operations - specialized API\naccessible from programming language of choice which is either interpreted\nor compiled for specific platform (possibilities are limited by what the API has to offer)\n* the use of HTML+CSS on all platforms (discarding the possible advantages offered by\nnative solutions on mobile devices) \n\nBut there is a third way, where only presentation logic code is shared and UI rendering\nand IO stays native. Where iOS, Web and Android developers can customize the app in every\ndetail.\n\nThis approach seems to be the most demanding one in terms of software architecture. Common business\nlogic cannot longer be expressed in purely technical terms of low-level events, requests,\nresponses or threads. Mouse clicks or touch events are becoming a\nstream of semantically defined user intents like \"select element\". HTTP requests to remote services\nare becoming a streams of domain data being provided asynchronously in any moment.\nConcurrency is handled by declaring *what* to do leaving *when*\nand *how* to reactive framework. Such an approach, even though more challenging conceptually,\nis worth the effort. Abstracting app logic from specific\nplatform brings much better overall architecture which pays off in the future when\nthe application grows.\n\nAs there is no blueprint from Google on how to build applications like Google Inbox, I decided\nto use my whole experience to \"reverse engineer\" possible approach and provide such a minimal project.\nI hope to push it even further in terms of reactive\nprogramming on top of RxJava as it is quite popular on Android, there is a GWT port, and apparently\nit is possible to transpile the whole library to Objective-C.\n\nIt does not matter so much what this application is doing and if it is useful at all.\nI did not want to provide any backend component and struggle with deployment. Therefore I decided\nto display data loaded from one of public APIs available on the Internet and\n[GitHub users search API](https://developer.github.com/v3/search/#search-users)\nwill serve as a good example.\n\n# Use case\n\nAs application user I want to submit query to search for GitHub users \nso that relevant user list will be displayed.\n\n# Architecture\n\nThis project provides conceptual presentation logic without actual UI code bound to any platform.\n\nFor the platform specific code see:\n\n* [github-users-web](https://github.com/xemantic/github-users-web)\n* [github-users-android](https://github.com/xemantic/github-users-android)\n* [github-users-ios](https://github.com/xemantic/github-users-ios)\n\nTechnically it is a library containing Java code which will be transpiled either\nto JavaScript ([GWT](http://www.gwtproject.org/)) or to\nObjective-C ([J2ObjC](https://developers.google.com/j2objc/))\ncode for Web and iOS platform respectively. In case of\nAndroid platform the Java code can be used directly.\n\n## Dependencies\n\nOnly minimal set of Java 8 classes is used plus:\n\n* [javax-inject](http://javax-inject.github.io/javax-inject/) - JSR-330 Dependency Injection\n* [RxJava](https://github.com/ReactiveX/RxJava) - Reactive Extensions for the JVM\n* [junit](http://junit.org/junit4/) - JUnit is a simple framework to write repeatable tests\n* [mockito](http://site.mockito.org/) - Tasty mocking framework for unit tests in Java\n* [AssertJ](http://joel-costigliola.github.io/assertj/) - Fluent assertions for java \n\nThese popular dependencies will either have emulation on all the platforms or\nbe transpiled to the native code.\n\n## Model-View-Presenter\n\nBasically view is dumb and can be mocked while presenter has testable logic.\n\nSee [Model-View-Presenter article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter)\narticle on Wikipedia.\n\n## Reactive paradigm\n\nRxJava is a crucial component of this solution providing:\n \n* event distribution mechanism decoupling presenters and therefore also associated visual components\n* handling of asynchronous responses from remote web services\n* abstracting the way how UI events are streamed to the presenter logic\n\n## Events\n\nApplication events are defined in the\n[com.xemantic.githubusers.logic.event](src/main/java/com/xemantic/githubusers/logic/event/)\npackage. Thanks to being separated from the rest of application logic, they can be\neasily used to decouple components (presenter logic).\n\nEvent distribution is based on event channels where:\n\n* publisher is injected with [Sink](src/main/java/com/xemantic/githubusers/logic/event/Sink.java)\n* subscriber is injected with `Observable`\n\nNote: the original design was based on the EventBus concept, but @ibaca pointed out that thanks\nto typed injections, it is possible to eliminate explicit EventBus completely.\n\n### Emitting Events\n\n```java\npublic class FooPresenter {\n  \n  @Inject\n  public FooPresenter(FooView view, Sink\u003cBarEvent\u003e barSink) {\n    super(\n      view.click$()\n          .onNext(e -\u003e barSink.publish(new BarEvent(\"foo\")))  \n    );\n  }\n  \n}\n```\n\nNote: several `Sink`s of different event types can be injected.\n\n### Receiving Events\n \n```java\npublic class BuzzPresenter {\n  \n  @Inject\n  public BuzzPresenter(BuzzView view, Observable\u003cBarEvent\u003e barEvent$) {\n    super(\n      barEvent$.onNext(e -\u003e view.display(e.getMessage()))  \n    );\n  }\n  \n}\n```\n\nNote: several `Observable`s of different event types can be injected. \n\n### Application Events vs Platform UI Events\n\nThe only events defined in this project are application events. Platform events specific\nto UI will always come out of View interfaces and their `observe` + _Intent_ methods.\n\nMany UI events, like specific user intent received via click or touch event, will not\ncarry any payload. They will be just marked with\n[Trigger](src/main/java/com/xemantic/githubusers/logic/event/Trigger.java) as an event\ntype.\n\n### Presenter Lifecycle and Events\n\nWhen presenter is started it will usually:\n\n* subscribe to general application events\n* subscribe to events generated by UI actions\n\nThe presenter logic defines how to react to these events, it might:\n\n* display something on view\n* call external service\n* change internal presenter state\n* publish general application events to be received by decoupled event consumers\n\nMost of these operations are easily testable with mocked view.\n \n## Service Access Layer\n\n### Service\n\nIs provided exclusively by interfaces [UserSearchService](src/main/java/com/xemantic/githubusers/logic/service/UserService.java)\nwhich returns `Observable` (technically `Single`) of [SearchResult](src/main/java/com/xemantic/githubusers/logic/model/SearchResult.java)\nholding also the list of [User](src/main/java/com/xemantic/githubusers/logic/model/User.java)s.\n\nServices can be implemented using:\n\n* [Retrofit + RxJava](http://square.github.io/retrofit/) on Android\n* [AutoREST for GWT](https://github.com/intendia-oss/autorest)\n\n### Model\n\nThe structure of `SearchResult` interface reflects\n[JSON structure of GitHub API response](https://developer.github.com/v3/search/#search-users).\n\nWhen implementing these entities various methods might be used like\n\n* GSON/jackson json parser for android\n* `@JsInterop` annotations for GWT\n\n## View\n\nSee [com.xemantic.githubusers.logic.view](src/main/java/com/xemantic/githubusers/logic/view)\npackage.\n\n## Presenter\n\nSee [com.xemantic.githubusers.logic.presenter](src/main/java/com/xemantic/githubusers/logic/presenter)\npackage.\n\nExpectations for these presenters are visible in their\n[test cases](src/test/java/com/xemantic/githubusers/presenter)\nwhich account for most code in this project.\n\n## Testing\n\nBy following MVP principles all the views are prepared in the way they can be mocked and\nassumptions can be made against their state in the unit tests. Ready presenters are\ncoming with full test coverage and test cases can be transpiled as well to be run again on\nthe target platform. See example\n[UserPresenterTest](src/test/java/com/xemantic/githubusers/presenter/UserPresenterTest.java).\n\nNote: end your test cases with:\n\n```java\n  InOrder inOrder = inOrder(mock1, mock2);\n  // ...\n  // method verifications\n  // ...\n  verifyNoMoreInteractions(mock1, mock2);\n  inOrder.verifyNoMoreInteractions();\n```\n\nThis verification order gives much more convenient error messages.\n\n# User Experience design\n\nThe [Material Design](https://material.io/guidelines/) will be used on all the platforms\nwith help of [Material Components](https://material.io/components/).\n\n# Versioning\n\nThis project is following [Semantic Versioning](http://semver.org/) scheme\nwith 3 decimal numbers separated by dots. All 3 version numbers\n(major, minor, bugfix) should be always present, which implies that for\nmajor and minor releases bugfix version will be set to 0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxemantic%2Fgithub-users","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxemantic%2Fgithub-users","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxemantic%2Fgithub-users/lists"}