Skip to content

Commit a5b8953

Browse files
committed
chore: initial upload
1 parent 53cce7c commit a5b8953

File tree

5 files changed

+357
-0
lines changed

5 files changed

+357
-0
lines changed

PowerSyncSwift/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

PowerSyncSwift/Package.resolved

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PowerSyncSwift/Package.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// swift-tools-version: 5.7
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "PowerSyncSwift",
8+
platforms: [
9+
.iOS(.v13),
10+
.macOS(.v10_13)
11+
],
12+
products: [
13+
// Products define the executables and libraries a package produces, making them visible to other packages.
14+
.library(
15+
name: "PowerSyncSwift",
16+
targets: ["PowerSyncSwift"]),
17+
],
18+
dependencies: [
19+
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA5.0"),
20+
.package(url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git", "0.3.1"..<"0.4.0"),
21+
],
22+
targets: [
23+
// Targets are the basic building blocks of a package, defining a module or a test suite.
24+
// Targets can depend on other targets in this package and products from dependencies.
25+
.target(
26+
name: "PowerSyncSwift",
27+
dependencies: [
28+
.product(name: "PowerSync", package: "powersync-kotlin"),
29+
.product(name: "PowerSyncSQLiteCore", package: "powersync-sqlite-core-swift")
30+
]),
31+
.testTarget(
32+
name: "PowerSyncSwiftTests",
33+
dependencies: ["PowerSyncSwift"]
34+
),
35+
]
36+
)
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
import Foundation
2+
import PowerSync
3+
4+
typealias SuspendHandle = () async throws -> Any?
5+
6+
class PowerSync {
7+
let factory = DatabaseDriverFactory()
8+
var db: PowerSyncDatabase!
9+
10+
func openDb() {
11+
db = PowerSyncDatabase(factory: factory, schema: schema, dbFilename: "powersync-swift.sqlite")
12+
}
13+
14+
func watch <T: Codable> (_ sql: String, parameters: [String: Any] = [:], mapper: @escaping (Cursor) throws -> T) async -> AnyPublisher<[T], Never> {
15+
}
16+
17+
func execute(_ sql: String, parameters: [String: Any] = [:]) async throws {
18+
try await db.execute(sql: sql, parameters: parameters)
19+
}
20+
21+
func writeTransaction(_ queryHandle: @escaping () async throws -> Any) async throws -> Any? {
22+
try await db.writeTransaction(callback: SuspendTaskWrapper(queryHandle))
23+
}
24+
25+
func readTransaction(_ queryHandle: @escaping () async throws -> Any) async throws -> Any? {
26+
try await db.readTransaction(callback: SuspendTaskWrapper(queryHandle))
27+
}
28+
29+
30+
31+
func insertList(_ list: NewListContent) async throws {
32+
try await self.db.execute(
33+
sql: "INSERT INTO \(LISTS_TABLE) (id, created_at, name, owner_id) VALUES (uuid(), datetime(), ?, ?)",
34+
parameters: [list.name, connector.currentUserID]
35+
)
36+
}
37+
38+
func deleteList(id: String) async throws {
39+
try await db.writeTransaction(callback: SuspendTaskWrapper {
40+
try await self.db.execute(
41+
sql: "DELETE FROM \(LISTS_TABLE) WHERE id = ?",
42+
parameters: [id]
43+
)
44+
try await self.db.execute(
45+
sql: "DELETE FROM \(TODOS_TABLE) WHERE list_id = ?",
46+
parameters: [id]
47+
)
48+
return
49+
})
50+
}
51+
52+
func watchTodos(_ listId: String, _ cb: @escaping (_ todos: [Todo]) -> Void ) async {
53+
for await todos in self.db.watch(
54+
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
55+
parameters: [listId],
56+
mapper: { cursor in
57+
return Todo(
58+
id: cursor.getString(index: 0)!,
59+
listId: cursor.getString(index: 1)!,
60+
photoId: cursor.getString(index: 2),
61+
description: cursor.getString(index: 3)!,
62+
isComplete: cursor.getBoolean(index: 4)! as! Bool,
63+
createdAt: cursor.getString(index: 5),
64+
completedAt: cursor.getString(index: 6),
65+
createdBy: cursor.getString(index: 7),
66+
completedBy: cursor.getString(index: 8)
67+
)
68+
}
69+
) {
70+
cb(todos as! [Todo])
71+
}
72+
}
73+
74+
func insertTodo(_ todo: NewTodo, _ listId: String) async throws {
75+
try await self.db.execute(
76+
sql: "INSERT INTO \(TODOS_TABLE) (id, created_at, created_by, description, list_id, completed) VALUES (uuid(), datetime(), ?, ?, ?, ?)",
77+
parameters: [connector.currentUserID, todo.description, listId, todo.isComplete]
78+
)
79+
}
80+
81+
func updateTodo(_ todo: Todo) async throws {
82+
// Do this to avoid needing to handle date time from Swift to Kotlin
83+
if(todo.isComplete) {
84+
try await self.db.execute(
85+
sql: "UPDATE \(TODOS_TABLE) SET description = ?, completed = ?, completed_at = datetime(), completed_by = ? WHERE id = ?",
86+
parameters: [todo.description, todo.isComplete, connector.currentUserID, todo.id]
87+
)
88+
} else {
89+
try await self.db.execute(
90+
sql: "UPDATE \(TODOS_TABLE) SET description = ?, completed = ?, completed_at = NULL, completed_by = NULL WHERE id = ?",
91+
parameters: [todo.description, todo.isComplete, todo.id]
92+
)
93+
}
94+
}
95+
96+
func deleteTodo(id: String) async throws {
97+
try await db.writeTransaction(callback: SuspendTaskWrapper {
98+
try await self.db.execute(
99+
sql: "DELETE FROM \(TODOS_TABLE) WHERE id = ?",
100+
parameters: [id]
101+
)
102+
return
103+
})
104+
}
105+
}
106+
107+
class SuspendTaskWrapper: KotlinSuspendFunction1 {
108+
let handle: () async throws -> Any
109+
110+
init(_ handle: @escaping () async throws -> Any) {
111+
self.handle = handle
112+
}
113+
114+
@MainActor
115+
func invoke(p1: Any?, completionHandler: @escaping (Any?, Error?) -> Void) {
116+
Task {
117+
do {
118+
let result = try await self.handle()
119+
completionHandler(result, nil)
120+
} catch {
121+
completionHandler(nil, error)
122+
}
123+
}
124+
}
125+
}
126+
import Foundation
127+
import PowerSync
128+
129+
typealias SuspendHandle = () async throws -> Any?
130+
131+
@Observable
132+
@MainActor
133+
class PowerSync {
134+
let factory = DatabaseDriverFactory()
135+
let connector = SupabaseConnector()
136+
let schema = AppSchema
137+
var db: PowerSyncDatabase!
138+
139+
func openDb() {
140+
db = PowerSyncDatabase(factory: factory, schema: schema, dbFilename: "powersync-swift.sqlite")
141+
}
142+
143+
// openDb must be called before connect
144+
func connect() async {
145+
do {
146+
try await db.connect(connector: connector, crudThrottleMs: 1000, retryDelayMs:5000, params: [:])
147+
} catch {
148+
print("Unexpected error: \(error.localizedDescription)") // Catches any other error
149+
}
150+
}
151+
152+
func version() async -> String {
153+
do {
154+
return try await db.getPowerSyncVersion()
155+
} catch {
156+
return error.localizedDescription
157+
}
158+
}
159+
160+
func signOut() async throws -> Void {
161+
try await db.disconnectAndClear(clearLocal: true)
162+
try await connector.client.auth.signOut()
163+
}
164+
165+
func writeTransaction(_ queryHandle: @escaping () async throws -> Any) async throws -> Any? {
166+
try await db.writeTransaction(callback: SuspendTaskWrapper(queryHandle))
167+
}
168+
169+
func readTransaction(_ queryHandle: @escaping () async throws -> Any) async throws -> Any? {
170+
try await db.readTransaction(callback: SuspendTaskWrapper(queryHandle))
171+
}
172+
173+
func watchLists(_ cb: @escaping (_ lists: [ListContent]) -> Void ) async {
174+
for await lists in self.db.watch(
175+
sql: "SELECT * FROM \(LISTS_TABLE)",
176+
parameters: [],
177+
mapper: { cursor in
178+
ListContent(
179+
id: cursor.getString(index: 0)!,
180+
name: cursor.getString(index: 1)!,
181+
createdAt: cursor.getString(index: 2)!,
182+
ownerId: cursor.getString(index: 3)!
183+
)
184+
}
185+
) {
186+
cb(lists as! [ListContent])
187+
}
188+
}
189+
190+
func insertList(_ list: NewListContent) async throws {
191+
try await self.db.execute(
192+
sql: "INSERT INTO \(LISTS_TABLE) (id, created_at, name, owner_id) VALUES (uuid(), datetime(), ?, ?)",
193+
parameters: [list.name, connector.currentUserID]
194+
)
195+
}
196+
197+
func deleteList(id: String) async throws {
198+
try await db.writeTransaction(callback: SuspendTaskWrapper {
199+
try await self.db.execute(
200+
sql: "DELETE FROM \(LISTS_TABLE) WHERE id = ?",
201+
parameters: [id]
202+
)
203+
try await self.db.execute(
204+
sql: "DELETE FROM \(TODOS_TABLE) WHERE list_id = ?",
205+
parameters: [id]
206+
)
207+
return
208+
})
209+
}
210+
211+
func watchTodos(_ listId: String, _ cb: @escaping (_ todos: [Todo]) -> Void ) async {
212+
for await todos in self.db.watch(
213+
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
214+
parameters: [listId],
215+
mapper: { cursor in
216+
return Todo(
217+
id: cursor.getString(index: 0)!,
218+
listId: cursor.getString(index: 1)!,
219+
photoId: cursor.getString(index: 2),
220+
description: cursor.getString(index: 3)!,
221+
isComplete: cursor.getBoolean(index: 4)! as! Bool,
222+
createdAt: cursor.getString(index: 5),
223+
completedAt: cursor.getString(index: 6),
224+
createdBy: cursor.getString(index: 7),
225+
completedBy: cursor.getString(index: 8)
226+
)
227+
}
228+
) {
229+
cb(todos as! [Todo])
230+
}
231+
}
232+
233+
func insertTodo(_ todo: NewTodo, _ listId: String) async throws {
234+
try await self.db.execute(
235+
sql: "INSERT INTO \(TODOS_TABLE) (id, created_at, created_by, description, list_id, completed) VALUES (uuid(), datetime(), ?, ?, ?, ?)",
236+
parameters: [connector.currentUserID, todo.description, listId, todo.isComplete]
237+
)
238+
}
239+
240+
func updateTodo(_ todo: Todo) async throws {
241+
// Do this to avoid needing to handle date time from Swift to Kotlin
242+
if(todo.isComplete) {
243+
try await self.db.execute(
244+
sql: "UPDATE \(TODOS_TABLE) SET description = ?, completed = ?, completed_at = datetime(), completed_by = ? WHERE id = ?",
245+
parameters: [todo.description, todo.isComplete, connector.currentUserID, todo.id]
246+
)
247+
} else {
248+
try await self.db.execute(
249+
sql: "UPDATE \(TODOS_TABLE) SET description = ?, completed = ?, completed_at = NULL, completed_by = NULL WHERE id = ?",
250+
parameters: [todo.description, todo.isComplete, todo.id]
251+
)
252+
}
253+
}
254+
255+
func deleteTodo(id: String) async throws {
256+
try await db.writeTransaction(callback: SuspendTaskWrapper {
257+
try await self.db.execute(
258+
sql: "DELETE FROM \(TODOS_TABLE) WHERE id = ?",
259+
parameters: [id]
260+
)
261+
return
262+
})
263+
}
264+
}
265+
266+
class SuspendTaskWrapper: KotlinSuspendFunction1 {
267+
let handle: () async throws -> Any
268+
269+
init(_ handle: @escaping () async throws -> Any) {
270+
self.handle = handle
271+
}
272+
273+
@MainActor
274+
func invoke(p1: Any?, completionHandler: @escaping (Any?, Error?) -> Void) {
275+
Task {
276+
do {
277+
let result = try await self.handle()
278+
completionHandler(result, nil)
279+
} catch {
280+
completionHandler(nil, error)
281+
}
282+
}
283+
}
284+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Testing
2+
@testable import PowerSyncSwift
3+
4+
@Test func example() async throws {
5+
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
6+
}

0 commit comments

Comments
 (0)