{"id":15118674,"url":"https://github.com/zz129869523/PocketBase","last_synced_at":"2025-09-28T01:30:26.822Z","repository":{"id":202836987,"uuid":"582288769","full_name":"zz129869523/PocketBase","owner":"zz129869523","description":"A simple Swift client for PocketBase.","archived":false,"fork":false,"pushed_at":"2023-11-12T04:57:05.000Z","size":120,"stargazers_count":48,"open_issues_count":2,"forks_count":9,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-09-26T02:01:28.340Z","etag":null,"topics":["ios","pocketbase","swift","swiftui"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/zz129869523.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-26T10:50:05.000Z","updated_at":"2024-09-10T05:04:58.000Z","dependencies_parsed_at":"2024-09-26T02:10:54.201Z","dependency_job_id":null,"html_url":"https://github.com/zz129869523/PocketBase","commit_stats":null,"previous_names":["zz129869523/pocketbase"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zz129869523%2FPocketBase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zz129869523%2FPocketBase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zz129869523%2FPocketBase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zz129869523%2FPocketBase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zz129869523","download_url":"https://codeload.github.com/zz129869523/PocketBase/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234475315,"owners_count":18839358,"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":["ios","pocketbase","swift","swiftui"],"created_at":"2024-09-26T01:53:33.837Z","updated_at":"2025-09-28T01:30:26.444Z","avatar_url":"https://github.com/zz129869523.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":["Node.js"],"readme":"# PocketBase\n\nThe specific usage is similar to that provided by [pocketbase](https://pocketbase.io/docs/client-side-integration/).\n\n## Feature\n- List/Search\n- View\n- Create\n- Update\n- Delete\n- Realtime\n- Auth\n- File upload\n\n## Demo\n\n[Demo](./Example) App Design Reference Youtuber [Kavsoft](https://www.youtube.com/@Kavsoft)'s [SwiftUI Social Media App](https://www.youtube.com/watch?v=-pAQcPolruw\u0026list=PLimqJDzPI-H9u3cSJCPB_EJsTU8XP2NUT)\n\nhttps://user-images.githubusercontent.com/16719064/215251448-41018bb5-a115-473f-a56a-7156b2c3875e.mp4\n\n## Getting Started\n\n### As you are all aware\nMake sure to enable PocketBase first, please refer to [pocketbase](https://github.com/pocketbase/pocketbase) for specific usage methods\n\n### Install\n\n#### Swift Package Manager\n1. Insert url of this SPM in your XCode Project with `File → Add Package → Copy Dependency`.\n2. Because of EventSource, you can only choose to use branch main at present.\n```\nhttps://github.com/zz129869523/PocketBase\n```\n\n2. Import the framework:\n``` swift\nimport PocketBase\n```\n\n### Usage\n#### Application url \n``` swift\nlet client = PocketBase\u003cUser\u003e() // default is http://0.0.0.0:8090\nlet client = PocketBase\u003cUser\u003e(host: \"https://your_domain\")\n```\n\n#### Custom authStore\nIf you want to customize the default authStore(User), you can extend it and pass a new custom struct to the client:\n``` swift\nstruct CustomUser: AuthModel { // implement AuthModel\n  var id: String?\n  var collectionId: String?\n  var collectionName: String?\n  var created: String?\n  var updated: String?\n  var username: String?\n  var verified: Bool?\n  var emailVisibility: Bool?\n  var email: String?\n  var name: String?\n  var avatar: String?\n  \n  var deactivation: Bool?\n}\n\nlet client = PocketBase\u003cCustomUser\u003e()\n\nprint(client.authStore.model?.deactivation ?? false)\n```\n\n#### Instance\n``` swift\n@main\nstruct ChartTestApp: App {\n  var body: some Scene {\n    WindowGroup {\n      ContentView()\n        .environmentObject(PocketBase\u003cUser\u003e()) // \u003c-- 1\n    }\n  }\n}\n\nstruct ContentView: View {\n  @EnvironmentObject var client: PocketBase\u003cUser\u003e // \u003c-- 2\n\n  var body: some View {\n    // ...\n  }\n}\n```\nor \n``` swift\nstruct ContentView: View {\n  let client = PocketBase\u003cUser\u003e() // choose one of three\n  @StateObject var client = PocketBase\u003cUser\u003e() // choose one of three\n  @ObservedObject var client = PocketBase\u003cUser\u003e() // choose one of three\n\n  var body: some View {\n    // ...\n  }\n}\n```\n\n#### List/Search\n``` swift\nstruct Post: Codable, Identifiable { // or implement BaseModel\n  var id: String?\n  var title: String\n}\n\nextension Post {\n  init(dictionary: [String: Any]) throws {\n    self = try JSONDecoder().decode(Self.self, from: JSONSerialization.data(withJSONObject: dictionary))\n  }\n}\n\nTask {\n  // getList\n  let dic = await client.collection(\"posts\").getList() // return type of [String : Any]?\n\n  if let dic {\n    let err = try? ErrorResponse(dictionary: dic)\n    // Handle errors such as status code 400 404 \n    print(err?.code)\n    print(err?.message)\n    \n    if err == nil, let result: ListResult\u003cPost\u003e = try? Post(dictionary: dic) {\n      print(result.items)\n    }\n  }\n\n  // or \n\n  let posts: ListResult\u003cPost\u003e? = await client.collection(\"posts\").getList()\n  if let listResult: ListResult\u003cPost\u003e = await client.collection(\"posts\").getList() {\n    self.posts = listResult.items\n  }\n\n  // getFullList\n  let posts: [Post] = await client.collection(\"posts\").getFullList(batch: 200, filter: \"created\u003c'2022-12-26 11:10:08'\", sort: \"-created\")\n\n  // getFirstListItem\n  let post: Post? = await client.collection(\"posts\").getFirstListItem()\n  if let post: Post = await client.collection(\"posts\").getFirstListItem() {\n    // ...\n  }\n}\n```\n\n#### View\n``` swift\nTask {\n  let post: Post? = await client.collection(\"posts\").getOne(id: \"RECORD_ID\")\n}\n```\n\n#### Create\n``` swift\nTask {\n  let post: Post? = await client.collection(\"posts\").create([\"title\": \"hello pocketbase\"])\n  let post: Post? = await client.collection(\"posts\").create(Post(title: \"hello pocketbase\"))\n\n  // If you wanna upload file you should add MultipartFormData in your sturct.\n  // And use File struct.\n  // Like: \n  struct PostRequest: Codable, Identifiable, MultipartFormData { // \u003c- Add MultipartFormData\n    var id: String?\n    var title: String\n    var image: File // \u003c- This\n    var imageOptional: File? // Optional\n    var images: [File] // \u003c- Or like this\n    var imagesOptional: [File]? // Optional\n  }\n\n  let file = File(mimeType: \"image/jpeg\", filename: \"img1\", data: photoDataOrOtherData)\n\n  // Upload post with image file\n  let post: Post? = await client.collection(\"posts\").create(PostRequest(title: \"hello pocketbase\", image: file, images: [file, file, file]))\n\n  // Download URL is http://127.0.0.1:8090/api/files/COLLECTION_ID_OR_NAME/RECORD_ID/FILENAME\n\n  // returns something like:\n  // http://127.0.0.1:8090/api/files/posts/amqb484dme8ujz4/img1_52iWbGinWd.jpg\n  if let record: [String: Any] = await client.collection(\"posts\").getOne(id: \"amqb484dme8ujz4\") {\n    let filename = record[\"image\"] as! String\n    print(client.getFileUrl(record, filename))\n  }\n  // or struct Post implement BaseModel\n  struct Post: Codable, BaseModel { // \u003c- BaseModel is required\n    var id: String?\n    var collectionId: String?\n    var collectionName: String?\n    var created: String?\n    var updated: String?\n    var title: String\n    var image: String\n    var imageOptional: String?\n    var images: [String]\n    var imagesOptional: [String]?\n  }\n\n  // returns something like:\n  // http://127.0.0.1:8090/api/files/posts/amqb484dme8ujz4/img1_52iWbGinWd.jpg?thumb=200x200\n  if let record: Post = await client.collection(\"posts\").getOne(id: \"amqb484dme8ujz4\") {\n    let filename = record.image\n    print(client.getFileUrl(record, filename, query: [\"thumb\": \"200x200\"]))\n  }\n}\n```\n\n#### Update\n``` swift\nTask {\n  let post: Post? = await client.collection(\"posts\").update(\"RECORD_ID\", body: [\"title\": \"hello pocketbase\"])\n  let post: Post? = await client.collection(\"posts\").update(\"RECORD_ID\", body: Post(title: \"hello pocketbase\"))\n}\n```\n\n#### Delete\n``` swift\nTask {\n  let dic = await client.collection(\"posts\").delete(\"RECORD_ID\")\n}\n```\n\n#### Realtime\n``` swift\n// Subscribe to changes only in the specified record\nclient.collection(\"posts\").subscribe(\"RECORD_ID\") { dict in\n  print(dict)\n}\n\n// Subscribe to changes in any posts record\nclient.collection(\"posts\").subscribe(\"*\") { dict in\n  if let result: Event\u003cPost\u003e = try? Utils.dictionaryToStruct(dictionary: dict ?? [:]) { // Optional: Event\u003cType\u003e Can use\n    switch result.action {\n    case .create:\n      self.posts.append(result.record)\n    case .update:\n      if let row = self.posts.firstIndex(where: { $0.id == result.record.id }) {\n        self.posts[row] = result.record\n      }\n    case .delete:\n      self.posts = self.posts.filter { $0.id != result.record.id }\n    }\n  }\n}\n\n// Unsubscribe\nclient.collection(\"posts\").unsubscribe(\"RECORD_ID\") // remove all \"RECORD_ID\" subscriptions\nclient.collection(\"posts\").unsubscribe(\"*\") // remove all \"*\" topic subscriptions\nclient.collection(\"posts\").unsubscribe() // remove all subscriptions in the collection\n```\n\n#### Auth\n``` swift\nTask {\n  // authWithPassword\n  let _ = await client.collection(\"users\").authWithPassword(\"pocketbase@test.email\", \"12345678\")\n  let response: AuthResponse\u003cUser\u003e? = await client.collection(\"users\").authWithPassword(\"pocketbase@test.email\", \"12345678\")\n  print(response?.token)\n  print(response?.record)\n\n  print(client.authStore.isValid)\n  print(client.authStore.token)\n  print(client.authStore.model.id)\n\n  // \"logout\" the last authenticated account\n  client.authStore.clear()\n\n  // authWithOAuth2\n  let _ = await client.collection(\"users\").authWithOAuth2(.google, code: \"CODE\", codeVerifier: \"VERIFIER\", redirectUrl: \"REDIRECT_URL\")\n\n  // authRefresh\n  let _ = await client.collection(\"users\").authRefresh()\n\n  // requestVerification\n  let _ = await client.collection(\"users\").requestVerification(\"email\")\n\n  // requestPasswordReset\n  let _ = await client.collection(\"users\").requestPasswordReset(\"email\")\n\n  // requestEmailChange\n  let _ = await client.collection(\"users\").requestEmailChange(\"email\")\n\n  // listAuthMethods\n  let authMethods: AuthMethods? = await client.collection(\"users\").listAuthMethods()\n  print(authMethods?.authProviders)\n\n  // listExternalAuths\n  let authMethod: [AuthMethod] = await client.collection(\"users\").listExternalAuths(\"id\")\n  print(authMethod)\n\n  // unlinkExternalAuth\n  let _ = await client.collection(\"users\").unlinkExternalAuth(\"id\", provider: .google)\n}\n```\n\n#### AuthStore\n``` swift\nprint(client.authStore.isValid)\nprint(client.authStore.token)\nprint(client.authStore.model.id)\n\n// \"logout\" the last authenticated account\nclient.authStore.clear()\n```\n\n### API\n``` swift\n// MARK: - List/Search\nfunc getList(page: Int, perPage: Int, filter: String, sort: String, expand: String) async -\u003e [String: Any]?\nfunc getList\u003cR: Codable\u003e(page: Int, perPage: Int, filter: String, sort: String, expand: String) async -\u003e ListResult\u003cR\u003e?\nfunc getFullList\u003cR: Codable\u003e(batch: Int, filter: String, sort: String, expand: String) async -\u003e [R]\nfunc getFirstListItem\u003cR: Codable\u003e(filter: String, sort: String, expand: String) async -\u003e R?\n\n// MARK: - View\nfunc getOne(id: String, expand: String) async -\u003e [String: Any]?\nfunc getOne\u003cR: Codable\u003e(id: String, expand: String) async -\u003e R?\n\n// MARK: - Create\nfunc create\u003cBodyType: Codable\u003e(_ body: BodyType) async -\u003e [String: Any]?\nfunc create\u003cBodyType: Codable \u0026 MultipartFormData\u003e(_ body: BodyType) async -\u003e [String: Any]?\nfunc create\u003cBodyType: Codable, R: Codable\u003e(_ body: BodyType) async -\u003e R?\nfunc create\u003cBodyType: Codable \u0026 MultipartFormData, R: Codable\u003e(_ body: BodyType) async -\u003e R?\n\n// MARK: - Update\nfunc update\u003cBodyType: Codable\u003e(_ id: String, body: BodyType, expand: String) async -\u003e [String: Any]?\nfunc update\u003cBodyType: Codable, R: Codable\u003e(_ id: String, body: BodyType, expand: String) async -\u003e R?\n\n// MARK: - Delete\nfunc delete(_ id: String) async -\u003e [String: Any]?\n\n// MARK: - Realtime\nfunc subscribe(_ recordId: String, completion: @escaping ([String: Any]?) -\u003e Void)\nfunc unsubscribe(_ recordId: String)\n\n// MARK: - Auth\nfunc authWithPassword(_ identity: String, _ password: String, _ expand: String) async -\u003e [String: Any]?\nfunc authWithPassword\u003cUserModel: AuthModel\u003e(_ identity: String, _ password: String) async -\u003e AuthResponse\u003cUserModel\u003e?\n\nfunc authWithOAuth2(_ provider: OAuthProvider, code: String, codeVerifier: String, redirectUrl: String, createData: [String: String], expand: String) async -\u003e [String: Any]?\nfunc authWithOAuth2\u003cUserModel: AuthModel\u003e(_ provider: OAuthProvider, code: String, codeVerifier: String, redirectUrl: String, createData: [String: String], expand: String) async -\u003e AuthResponse\u003cUserModel\u003e?\n\nfunc authRefresh(expand: String) async -\u003e [String: Any]?\nfunc authRefresh\u003cUserModel: AuthModel\u003e(expand: String) async -\u003e AuthResponse\u003cUserModel\u003e?\n\nfunc requestVerification(_ email: String) async -\u003e [String: Any]?\nfunc requestPasswordReset(_ email: String) async -\u003e [String: Any]?\nfunc requestEmailChange(_ email: String) async -\u003e [String: Any]?\n\nfunc listAuthMethods() async -\u003e [String: Any]?\nfunc listAuthMethods() async -\u003e AuthMethods?\n\nfunc listExternalAuths(_ id: String) async -\u003e [String: Any]?\nfunc listExternalAuths(_ id: String) async -\u003e [AuthMethod]\n\nfunc unlinkExternalAuth(_ id: String, provider: OAuthProvider) async -\u003e [String: Any]?\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzz129869523%2FPocketBase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzz129869523%2FPocketBase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzz129869523%2FPocketBase/lists"}