{"id":17146701,"url":"https://github.com/championswimmer/spork","last_synced_at":"2025-04-13T10:31:08.171Z","repository":{"id":45330562,"uuid":"222056663","full_name":"championswimmer/SPORK","owner":"championswimmer","description":null,"archived":false,"fork":false,"pushed_at":"2021-12-20T18:04:22.000Z","size":14659,"stargazers_count":49,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-27T01:51:31.788Z","etag":null,"topics":["android","annotation-processor","annotations","gradle","kapt","kotlin","ksp"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/championswimmer.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}},"created_at":"2019-11-16T06:21:44.000Z","updated_at":"2025-01-01T17:02:36.000Z","dependencies_parsed_at":"2022-08-28T01:51:08.783Z","dependency_job_id":null,"html_url":"https://github.com/championswimmer/SPORK","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/championswimmer%2FSPORK","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/championswimmer%2FSPORK/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/championswimmer%2FSPORK/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/championswimmer%2FSPORK/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/championswimmer","download_url":"https://codeload.github.com/championswimmer/SPORK/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248698809,"owners_count":21147530,"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","annotation-processor","annotations","gradle","kapt","kotlin","ksp"],"created_at":"2024-10-14T21:09:23.965Z","updated_at":"2025-04-13T10:31:07.841Z","avatar_url":"https://github.com/championswimmer.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](docs/spork.png)\n\n\u003cp align=\"center\"\u003e \n  \u003cb\u003eS\u003c/b\u003ehared \u003cb\u003eP\u003c/b\u003ereferences \u003cb\u003eO\u003c/b\u003ebject \u003cb\u003eR\u003c/b\u003eelations in \u003cb\u003eK\u003c/b\u003eotlin \n  \u003cbr\u003e\u003cbr\u003e\n  \u003ca href=\"https://jitpack.io/#tech.arnav/SPORK\"\u003e\u003cimg src=\"https://jitpack.io/v/tech.arnav/SPORK.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nA Retrofit-inspired ORM for Android's SharedPreferences that turns an annotated interface into a SharedPreference accessor object.\n\n## Why ?\n\n### Old Way of Using SharedPreferences\nThere are multiple ORMs for SQLite in Android, and they make it very easy to \ntreat `tables` and `rows` as `classes` and `objects`. \nBut still, there is a lot of local data we store in `SharedPreferences` \nand accessing/modifying them still requires writing a  couple of boilerplate lines - \n\n```kotlin\nval prefs = context.getSharedPreferences('app_prefs', Context.MODE_PRIVATE)\n\n// read\nval currCount = prefs.getInt('launch_count', 0) \n// ----------------\n\n// write\nval editor = prefs.edit()\neditor.putInt('launch_count', currCount + 1) \neditor.apply()\n// -- or if using android ktx -- \nprefs.edit {\n  putInt('launch_count', currCount + 1)\n}\n```\nIt is time to reduce all of that. \n\n### The New \\*\\*AWESOME\\*\\* way ! \n\n#### 1. Create your preference as abstract class\n\n_**AppPrefs.kt**_ \n\n```kotlin\nimport tech.arnav.spork.annotations.Pref\nimport tech.arnav.spork.annotations.PreferenceFile\n\n@PreferenceFile(\"app_prefs\")\nabstract class AppPrefs {\n\n    @Pref(\"count\")\n    abstract var count: Int\n\n    @Pref(\"foobar\")\n    abstract var fooBar: String\n}\n```\n\n#### 2. Access it from your Activity/Service\n\n_**MainActivity.kt**_\n\n```kt\nclass MainActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        val appPrefs = Spork.create(this, AppPrefs::class)\n        // write prefs\n        appPrefs.screenName = \"MAIN\" \n        appPrefs.count++\n        \n        // read prefs\n        Toast.makeText(this, \"Visit number \" + appPrefs.count, Toast.LENGTH_SHORT).show()\n\n    }\n}\n```\n\n## Other Benefits\n\nOver time in large long running projects, you might have added prefs earlier, \nthat are not used anymore. Your users might be having huge pref files with ages old \nkeys not used anymore. But every time you do `getSharedPreferences` the \nentire file is still read into memory. \n\nWhile initialising, **SPORK** will automatically remove old keys that are not \nin `AppPrefs.kt` anymore. Thus your `app_prefs.xml` is always an \nup-to-date and pruned implementation of `AppPrefs.kt`.\n\n\n## Installation \n\nAdd this into your app's build.gradle \n\n```groovy\nrepositories {\n    // ...\n    maven { url 'https://jitpack.io' }\n}\n\ndependencies {\n    implementation 'tech.arnav.SPORK:spork-annotations:0.2.3'\n    kapt 'tech.arnav.SPORK:spork-compiler:0.2.3'\n}\n```\n\n#### Using reflection instead \n\u003e NOTE: Reflection has a huge runtime cost, and it is better to use the build time code generation\n\nIf you want to use reflection instead of kapt\n```groovy\nrepositories {\n    // ...\n    maven { url 'https://jitpack.io' }\n}\n\ndependencies {\n    implementation 'tech.arnav.SPORK:spork-reflect:0.2.3'\n}\n```\n\n## How ? \n\n### What happens under the hood ? \nA very simply concrete class is generated for each abstract class you craete \n\nThe abstract class you create - \n```kotlin\n@PreferenceFile(\"app_prefs\")\nabstract class AppPrefs {\n\n    @Pref(\"count\")\n    abstract var count: Int\n\n    @Pref(\"foobar\")\n    abstract var fooBar: String\n}\n```\n\nThe class that is generated \n```kotlin\nclass AppPrefsImpl(\n  context: Context\n) : AppPrefs() {\n  val prefs: SharedPreferences = context.getSharedPreferences(\"app_prefs\", Context.MODE_PRIVATE)\n\n  override var count: Int\n    get() = prefs.getInt(\"count\", 0) ?: 0\n    set(value) {\n      prefs.edit().putInt(\"count\", value).apply()\n    }\n\n  override var fooBar: String\n    get() = prefs.getString(\"foobar\", \"\") ?: \"\"\n    set(value) {\n      prefs.edit().putString(\"foobar\", value).apply()\n    }\n}\n```\n\n\n## TODO List\n\n- [x] Annotation Processor / Codegen support\n- [x] remove kotlin-reflect \n- [ ] allow a way to mark default get values if pref not already present\n- [ ] allow turning on/off pruning of old pref keys \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchampionswimmer%2Fspork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchampionswimmer%2Fspork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchampionswimmer%2Fspork/lists"}