{"id":18917786,"url":"https://github.com/tus/tuskit","last_synced_at":"2025-04-05T07:04:47.385Z","repository":{"id":7531275,"uuid":"8882941","full_name":"tus/TUSKit","owner":"tus","description":"The tus client for iOS.","archived":false,"fork":false,"pushed_at":"2024-03-04T15:51:12.000Z","size":6196,"stargazers_count":198,"open_issues_count":3,"forks_count":113,"subscribers_count":16,"default_branch":"main","last_synced_at":"2024-03-27T02:13:22.496Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://tus.io/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null}},"created_at":"2013-03-19T16:12:44.000Z","updated_at":"2024-04-15T12:03:16.677Z","dependencies_parsed_at":"2023-11-30T16:30:46.047Z","dependency_job_id":"0e27cf30-5f54-48dc-ae44-7181508abd9c","html_url":"https://github.com/tus/TUSKit","commit_stats":{"total_commits":319,"total_committers":23,"mean_commits":"13.869565217391305","dds":0.6332288401253918,"last_synced_commit":"684cc8ad1fc7d55403a7e7814c3d1920fc8c576a"},"previous_names":["tus/tus-ios-client"],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tus%2FTUSKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tus%2FTUSKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tus%2FTUSKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tus%2FTUSKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tus","download_url":"https://codeload.github.com/tus/TUSKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299831,"owners_count":20916190,"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":[],"created_at":"2024-11-08T10:28:08.978Z","updated_at":"2025-04-05T07:04:47.368Z","avatar_url":"https://github.com/tus.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TUSKit\n\nAn iOS client written in `Swift` for [TUS resumable upload protocol](http://tus.io/).\n\n[![Protocol](http://img.shields.io/badge/tus_protocol-v1.0.0-blue.svg?style=flat)](http://tus.io/protocols/resumable-upload.html)\n[![Version](https://img.shields.io/cocoapods/v/TUSKit.svg?style=flat)](http://cocoadocs.org/docsets/TUSKit)\n[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-4BC51D.svg?style=flat)](https://swift.org/package-manager/)\n[![License](https://img.shields.io/cocoapods/l/TUSKit.svg?style=flat)](http://cocoadocs.org/docsets/TUSKit)\n[![Platform](https://img.shields.io/cocoapods/p/TUSKit.svg?style=flat)](http://cocoadocs.org/docsets/TUSKit)\n\nWith this client, you can upload regular raw `Data` or file-paths. \n\n## Usage\n\nYou can refer to the example project to see how TUSKit is implemented. \n\nAs a starting point, please refer to the [SceneDelegate](TUSKitExample/TUSKitExample/SceneDelegate.swift).\n\nHere is how you can instantiate a `TUSClient` instance.\n\nBe sure to store a reference to the client somewhere. Then initialize as you normally would.\n``` swift\nfinal class MyClass {\n  let tusClient: TUSClient\n  \n  init() {\n      tusClient = TUSClient(server: URL(string: \"https://tusd.tusdemo.net/files\")!, sessionIdentifier: \"TUS DEMO\", storageDirectory: URL(string: \"TUS\")!)\n      tusClient.delegate = self\n  }\n}\n```\n\nNote that you can register as a delegate to retrieve the URL's of the uploads, and also any errors that may arise.\n\nNote that you *can* pass your own `URLSession` instance to the initializer.\n\nYou can conform to the `TUSClientDelegate` to receive updates from the `TUSClient`.\n\n```swift\nextension MyClass: TUSClientDelegate {\n    func didStartUpload(id: UUID, client: TUSClient) {\n        print(\"TUSClient started upload, id is \\(id)\")\n        print(\"TUSClient remaining is \\(client.remainingUploads)\")\n    }\n    \n    func didFinishUpload(id: UUID, url: URL, client: TUSClient) {\n        print(\"TUSClient finished upload, id is \\(id) url is \\(url)\")\n        print(\"TUSClient remaining is \\(client.remainingUploads)\")\n        if client.remainingUploads == 0 {\n            print(\"Finished uploading\")\n        }\n    }\n    \n    func uploadFailed(id: UUID, error: Error, client: TUSClient) {\n        print(\"TUSClient upload failed for \\(id) error \\(error)\")\n    }\n    \n    func fileError(error: TUSClientError, client: TUSClient) {\n        print(\"TUSClient File error \\(error)\")\n    }\n    \n    \n    func totalProgress(bytesUploaded: Int, totalBytes: Int, client: TUSClient) {\n    }\n    \n    \n    func progressFor(id: UUID, bytesUploaded: Int, totalBytes: Int, client: TUSClient) {\n\n    }\n    \n}\n```\n\n### Starting an upload\n\nA `TUSClient` can upload `Data` or paths to a file in the form of a `URL`.\n\nTo upload data, use the `upload(data:)` method`\n\n```swift\nlet data = Data(\"I am some data\".utf8)\nlet uploadId = try tusClient.upload(data: data)\n```\n\nTo upload multiple data files at once, use the `uploadMultiple(dataFiles:)` method.\n\nTo upload a single stored file, retrieve a file path and pass it to the client.\n\n```swift\nlet pathToFile:URL = ...\nlet uploadId = try tusClient.uploadFileAt(filePath: pathToFile)\n```\n\nTo upload multiple files at once, you can use the `uploadFiles(filePaths:)` method.\n\n## Custom upload URL and custom headers\n\nTo specify a custom upload URL (e.g. for TransloadIt) or custom headers to be added to a file upload, please refer to the `uploadURL` and `customHeaders` properties in the methods related to uploading. Such as: `upload`, `uploadFileAt`, `uploadFiles` or `uploadMultiple(dataFiles:)`.\n\n## Measuring upload progress\n\nTo know how many files have yet to be uploaded, please refer to the `remainingUploads` property.\n\nPlease note that there isn't a percentage supplied, since it's up to you to define what the starting point is of an upload.\nFor example. If you upload 10 files, and 3 are finished, then you are at 3/10. However, if during this upload you add 2 more, should that count as 3/12 or do you consider it a a fresh upload? So 0/9. It's up to you to define how finished uploads are counted when adding new uploads.\n\nFor byte level progress. Please implement the `TUSClientDelegate` protocol and set it as a the `delegate` property of `TUSClient`.\n\n## Upload id's and contexts\n\nBy starting an upload you will receive an id. These id's are also passed to you via if you implement the `TUSClientDelegate`.\nYou can use these id's to identify which files are finished or failed (but you can also use contexts for that, see below). You can also delete these files on failure if you want. You can also use these id's to retry a failed upload.\n\nNote that `TUSClient` will automatically retry an upload a few times, but will eventually give up, after which it will report an error. After which you can call the `retry` method and try again.\n\n## Contexts\n\nYou can use id's to monitor progress and perform other tasks, such as stopping uploads. But you can also pass a context with richer information. TUSKit will return this context through various delegate calls. This way you don't have to keep track of the status of upload id's. You can pass in a small object with information, and you receive this from TUSKit.\n\nSecurity notice: TUSKit will store this context on the disk next to other file metadata. This is to maintain the information between sessions.\n\n## Starting a new session \n\nAn upload can fail at any time. Even when an app is in the background.\n\nTherefore, after starting a new app session, we recommend you inspect any failed uploads that may have occurred and act accordingly.\nFor instance, you can decide to do something with the failed uploads such as retrying them, deleting them, or reporting to the user.\n\n\n```swift\nFor instance, here is how you can initialize the client and check its failed uploads. Note that we first fetch the id's, after which retry the uploads.\n  \ntusClient = TUSClient(server: URL(string: \"https://tusd.tusdemo.net/files\")!, sessionIdentifier: \"TUS DEMO\", storageDirectory: URL(string: \"/TUS\")!)\ntusClient.delegate = self\ntusClient.start()\n        \ndo {\n  // When starting, you can retrieve the locally stored uploads that are marked as failure, and handle those.\n  // E.g. Maybe some uploads failed from a last session, or failed from a background upload.\n  let ids = try tusClient.failedUploadIds()\n  for id in ids {\n    // You can either retry a failed upload...\n    try tusClient.retry(id: id)\n    // ...alternatively, you can delete them too\n    // tusClient.removeCacheFor(id: id)\n  }\n} catch {\n  // Could not fetch failed id's from disk\n}\n\n```\n\n## Background uploading\n\nWhen you incorporate background uploading, we strongly recommend you to inspect any failed uploads that may have occured in the background. Please refer to [Starting a new Session](#starting-a-new-session) for more information.\n\niOS can leverage a background URLSession to enable background uploads that continue when a user leaves your app or locks their device. For more information, take a look at Apple's docs on [background URLSession](https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background). The docs focus on downloads but uploads follow pretty much the exact same principles.\n\nTo make incorporating background uploads as straightforward as possible, TUSKit handles all the complex details of managing the URLSession and delegate callbacks. As a consumer of TUSKit all you need to do is leverage the new initializer on `TUSClient` as shown below:\n\n```swift\ndo {\n    tusClient = try TUSClient(\n        server: URL(string: \"https://tusd.tusdemo.net/files\")!,\n        sessionIdentifier: \"TUS DEMO\",\n        sessionConfiguration: .background(withIdentifier: \"com.TUSKit.sample\"),\n        storageDirectory: URL(string: \"/TUS\")!,\n        chunkSize: 0\n    )\n} catch {\n    // ...\n}\n```\n\nThe easiest way to set everything up is to pass a `URLSession.background` configuration to the `TUSClient`. If you require a different configuration or don't want any background support at all, you're free to pass a different configuration.\n\nBecause TUSKit can now have uploads running while your app is no longer actively in memory, you should always use the `getStoredUpload` method on `TUSClient` on app launch to retrieve all stored uploads and extract information about which uploads are currently completed. Afterwards you can call `cleanup` to allow `TUSClient` to remove metadata for completed items. See the sample app for more details.\n\n### Limitations and constraints for background uploads on iOS\n\nThe primary source for information on how background uploads work will always be [Apple's own documentation](https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background) on performing network calls in the background. TUSKit is limited and bound by what's possible and allowed by the system.\n\nIn practice, this means that you should always schedule all of your uploads while the app is in the foreground. Scheduling more uploads while an app is in the background is not allowed and will result in the system deprioritizing your requests. \n\nFor this reason, chunking is strongly discouraged when you intend to leverage background uploads since you would have to make several network calls, one after the other, to complete your upload.\n\nIt's also important to understand that iOS will automatically wait for your user to have a network connection before attempting to start your upload. This means your uploads might take a while to start. If a network connection drops _while_ an upload is in progress, iOS will attempt to re-upload your file automatically.\n\nBy default, iOS will retry the full upload instead of resuming where the upload has left off. Unfortunately we haven't found a way to prevent iOS from doing this. On iOS 17 Apple has adde support for resumable uploads. We're currently exploring how we can integrate TUSKit with this feature since both iOS and TUSKit rely on the same protocols.\n\n### Warning: information below is deprecated in TUSKit 3.2.0.\nAvailable from iOS13, you can schedule uploads to be performed in the background using the `scheduleBackgroundTasks()` method on `TUSClient`. \n\nScheduled tasks are handled by iOS. Which means that each device will decide when it's best to upload in the background. Such as when it has a wifi connection and late at night.\n\nAs an example from the `SceneDelegate` found in the example app, you can schedule them accordingly:\n\n```swift\nclass SceneDelegate: UIResponder, UIWindowSceneDelegate {\n\n    // ... snip\n    \n    func sceneDidEnterBackground(_ scene: UIScene) {\n        // Called as the scene transitions from the foreground to the background.\n        tusClient.scheduleBackgroundTasks()\n    }\n}\n```\n\n## TUS Protocol Extensions\nThe client assumes by default that the server implements the [Creation TUS protocol extension](https://tus.io/protocols/resumable-upload.html#protocol-extensions). If your server does not support that, please ensure to provide an empty array for the `supportedExtensions` parameter in the client initializer.\n\n## Example app\n\nPlease refer to the [example app](/TUSKitExample) inside this project to see how to add photos from a PHPicker, using SwiftUI. You can also use the `PHPicker` mechanic for UIKit.\n\n## Parallelism \n\nAt the time of writing, this client does not support TUS' concatenation option. \nIt does, however, automatically support parallel uploads in a single client. It does also support multiple clients.\n\n## Underlying Mechanics\n\nThe `TUSClient` will retry a failed upload two times (three total attempts) before reporting it as an error.\n\nThe `TUSClient` will try to upload a file fully, and if it gets interrupted (e.g. broken connection or app is killed), it will continue where it left of.\n\nThe `TUSClient` stores files locally to upload them. It will use the `storageDirectory` path that is passed in the initializer. Or create a default directory inside the documentsdir at /TUS .\n\nThe `TUSClient` will automatically removed locally stored files once their upload is complete.\n\n## Multiple instances\n\n`TUSClient` supports multiple instances for simultaneous unrelated uploads.\n\nWarning: Multiple clients should not share the same `storageDirectory`. Give each client their own directory to work from, or bugs may occur.\n\nPlease note that `TUSClient` since version 3.0.0 is *not* a singleton anymore. \n\nIf you strongly feel you want a singleton, you can still make one using the static keyword.\n\n```swift\nfinal class MyClass {\n  static let tusClient: TUSClient = ...\n}\n```\n\nBut we discourage you from doing so because it makes resetting between tests harder, and it becomes problematic in a multi-threaded environment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftus%2Ftuskit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftus%2Ftuskit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftus%2Ftuskit/lists"}