{"id":21689281,"url":"https://github.com/mgj/fetcher","last_synced_at":"2025-05-12T16:24:34.166Z","repository":{"id":60774061,"uuid":"90421116","full_name":"mgj/fetcher","owner":"mgj","description":"Network layer for cross-platform apps","archived":false,"fork":false,"pushed_at":"2018-02-03T20:43:14.000Z","size":1833,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-18T02:01:35.896Z","etag":null,"topics":["cache","caching","cross-platform","csharp","csharp-code","csharp-library","pcl","pcl-library"],"latest_commit_sha":null,"homepage":"","language":"C#","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/mgj.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-05T22:22:12.000Z","updated_at":"2020-06-27T22:11:07.000Z","dependencies_parsed_at":"2022-10-04T15:45:38.307Z","dependency_job_id":null,"html_url":"https://github.com/mgj/fetcher","commit_stats":null,"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgj%2Ffetcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgj%2Ffetcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgj%2Ffetcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgj%2Ffetcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mgj","download_url":"https://codeload.github.com/mgj/fetcher/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253774805,"owners_count":21962231,"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":["cache","caching","cross-platform","csharp","csharp-code","csharp-library","pcl","pcl-library"],"created_at":"2024-11-25T17:22:21.645Z","updated_at":"2025-05-12T16:24:34.138Z","avatar_url":"https://github.com/mgj.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fetcher\n\n[![Build status](https://ci.appveyor.com/api/projects/status/github/mgj/fetcher?svg=true)](https://ci.appveyor.com/project/mgj/fetcher)\n[![NuGet](https://img.shields.io/nuget/v/artm.fetcher.svg)](https://www.nuget.org/packages/artm.fetcher/)\n\nSimple Network layer for Xamarin apps\n\n## Introduction\n\nDoing network such as downloading a JSON file from a webserver can be difficult on mobile devices\n\n* You need to ensure you're using the platform-specific optimized network api calls to get SPDY, GZIP, HTTP/2, Connection Pooling etc.\n  * On iOS this means [NSUrlSession](https://developer.apple.com/reference/foundation/urlsession)\n  * On Android this means [OkHttp](http://square.github.io/okhttp/)\n* You need to have a retry mechanism if the server is unresponsive\n* You need to have a caching layer to\n  * Minimize network usage by caching redundant requests, as connections are often metered\n  * Improve performance\n  * Handle unresponive servers / endpoints / no internet connection\n* You need to handle the Cold Start problem: the very first time an app is started and there is no internet available. You should be able to ship your app with preloaded data for a given url.\n\n## Goals\nGoals for this project:\n\n * Simple and easy to use - Prefer reasonable opinionated defaults over complex interfaces or configuration\n * Stable and predictable - Unit tests helps with this, but please open an issue or pull request if you experience crashes or surprising behavior\n * Minimal dependencies - Keep the library as small as possible. Dont add big dependencies (E.g. Realm database backing store) unless we have to\n * Distribution and easy installation - CI builds and publishes the library using NuGet. Include the library in all your projects and you should be good to go\n\n## Install\n\n```\nFind the package in your IDE's nuget explorer\n\nor\n\nInstall-Package artm.fetcher\n\nor\n\nnuget install artm.fetcher\n\n```\n\nExample time!\n\n## Setup:\n\n```\n// 1. Instantiate these on Android / iOS. If using dependency injection, you can register them as singletons.\nIFetcherLoggerService loggerService = new FetcherLoggerService();\nIFetcherRepositoryStoragePathService path = new FetcherRepositoryStoragePathService();\nIFetcherWebService webService = new FetcherWebService();\nIFetcherRepositoryService repository = new FetcherRepositoryService(loggerService, path);\n\n// 2. Initialize the repository. This creates the underlying database and is safe to call on every app startup\nawait ((FetcherRepositoryService)repository).Initialize();\n\n// 3. Primary interface you should use from your Core/PCL project\nIFetcherService fetcher = new FetcherService(webService, repository, loggerService);\n```\n\n## Using:\n\n```\nString url = \"https://www.google.com\";\n\n// Try our hardest to give you *some* response for a given url. \n// If an url has been recently created or updated we get the response from the local cache.\n// If an url has NOT recently been created or updated we try to update \n// the response from the network. \n// If we cannot get the url from the network, and no cached data is available, we try to use preloaded data.\nIUrlCacheInfo response = await fetcher.FetchAsync(new Uri(url));\n\n// (Optional) Cold start: You can ship with preloaded data, and thus avoid\n// an initial requirement for an active internet connection\nawait fetcher.PreloadAsync(new FetcherWebRequest()\n{\n    Url = url\n}, new FetcherWebResponse() { Body = \"\u003chtml\u003eHello world!\u003c/html\u003e\" });\n\n// Dont like HTTP GET? Dont like the default cache expiration of 1 day? No problem!\nIUrlCacheInfo postResponse = await fetcher.FetchAsync(new FetcherWebRequest()\n{\n    Url = url,\n    Method = \"POST\",\n    Headers = new System.Collections.Generic.Dictionary\u003cstring, string\u003e(),\n    Body = @\"[{ \"\"myData\"\": \"\"data\"\" }]\",\n    ContentType = \"application/json; charset=utf-8\"\n}, TimeSpan.FromDays(14));\n```\n\n## Caching rules\n\nBy default, a new cache entry will be created (that is, a `FetchAsync` call is considered unique) if the `IFetcherWebRequest` (and accompanying `IUrlCacheInfo`) does not already exist. The default caching policy ONLY caches responses with a successful Http Status (200 - 300).\n\nYou can modify this behavior either when instantiating/registering the `IFetcherService`, or by passing a `IFetcherCachePolicy` to the `IFetcherService.FetchAsync()` method:\n\n```\nIFetcherService fetcher = new FetcherService(webService, repository, loggerService, new AlwaysCachePolicy());\n```\n\n`AlwaysCachePolicy` is included and will cache every response, regardless of the Http Status of the response. The default cache policy used is `OnlySuccessfulResponsesCachePolicy`\n\nNOTE: Regardless of which `IFetcherCachePolicy` implementation is used, nothing will be cached if the server could not be reached.\n\n## Searching the cache:\n\nYou can search through the cached items using the `IFetcherRepositoryService` interface. Example:\n\n```\n// Search cached entries by Url...\nIEnumerable\u003cIUrlCacheInfo\u003e info = await repository.GetUrlCacheInfoForRequest(new FetcherWebRequest\n{\n    Url = \"https://www.google.com\"\n});\n\n// ... or anything else\nIEnumerable\u003cIUrlCacheInfo\u003e info2 = await repository.GetUrlCacheInfoForRequest(new FetcherWebRequest\n{\n    Url = \"https://www.google.com\",\n    Method = \"POST\",\n    Headers = new Dictionary\u003cstring, string\u003e\n    {\n        {\"X-ZUMO-APPLICATION\" , \"Hello world!\" }\n    }\n});\n```\n\n`GetUrlCacheInfoForRequest()` has additional overloads, go explore :P\n\nFinally you can use the ID's from the underlying SQLite database:\n\n```\nIUrlCacheInfo info = await repository.GetUrlCacheInfoForId(15);\n```\n\nIf these are not sufficient, you can retrieve all cached items and do the search yourself:\n\n```\nIEnumerable\u003cIUrlCacheInfo\u003e info = await repository.GetAllUrlCacheInfo();\nIEnumerable\u003cIFetcherWebRequest\u003e request = await repository.GetAllWebRequests();\nIEnumerable\u003cIFetcherWebResponse\u003e response = await repository.GetAllWebResponses();\n```\n\n## Cleanup:\n\n```\nawait repository.DeleteEntriesOlderThan(14); // Days\n```\n\n## FAQ\n\nOn android if you get an error popup about denied access to local libraries, add a reference to System.Data.SQLite in your Android project. PLEASE OPEN AN ISSUE IF YOU STILL EXPERIENCE THIS!\n\n## For contributors\n\nConsider using the Playground.Touch and Playground.Droid projects if you want to experiment with the code. Both projects are set up with dependencies and references so that you can get started quicker - Pull requests are very welcome!\n\n\n## Get it here\n\nGitHub: [https://github.com/mgj/fetcher](https://github.com/mgj/fetcher)\n\nNuGet: [https://www.nuget.org/packages/artm.fetcher/](https://www.nuget.org/packages/artm.fetcher/)\n\n(Optional) MvvmCross Plugin GitHub: [https://github.com/mgj/MvvmCross-Plugins](https://github.com/mgj/MvvmCross-Plugins)\n\n(Optional) MvvmCross Plugin NuGet: [https://www.nuget.org/packages/artm.mvxplugins.fetcher/](https://www.nuget.org/packages/artm.mvxplugins.fetcher/)\n\n\n## License\n\n[Apache 2.0][apache]\n\n[apache]: https://www.apache.org/licenses/LICENSE-2.0.html","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgj%2Ffetcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmgj%2Ffetcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgj%2Ffetcher/lists"}