Skip to content

Commit c32e269

Browse files
authored
feat: add ParseSchema to SDK (#370)
* wip * more updates * more updates * addIndex and deleteIndex * read only fields and indexes * wip CLP * adding read checks * finished CLP * add inits to CLP * add more inits * doc nits * add async/await methods * add Combine methods * fixes * fix CLP * make all CLP methods public * improve docs * improve CLP * CLP protocol start * Using CLP protocol * improve CLP signatures * working ParseSchema * fix schema index * docs * allow SPI to build docs * docs update * more docs * Finish ParseCLP docs * nits * doc schema nits * Finished docs and added changelog * add deleteField to playgrounds * fix pending indexes * improve protected fields * test codecov * revert codecov * add some CLP tests * refactor * more CLP tests * call sights * more tests * finish CLP access tests * remove unnecessary code * organize ParseCLP * nit * nit * set protectedFields internal set * finish CLP tests * refactor ParseSchema * finish ParseSchema tests * increase coverage * remove unused code * remove more code * fix combine tests * add userField to CLP
1 parent 56ec801 commit c32e269

File tree

64 files changed

+4645
-119
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4645
-119
lines changed

.spi.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ builder:
33
configs:
44
- platform: ios
55
scheme: "ParseSwift (iOS)"
6+
documentation_targets: ["ParseSwift (iOS)"]
67
- platform: macos-xcodebuild
78
scheme: "ParseSwift (macOS)"
89
- platform: macos-xcodebuild-arm

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.5.0...main)
66
* _Contributing to this repo? Add info about your change here to be included in the next release_
77

8+
__New features__
9+
- Add ParseSchema, ParseCLP, and ParseFieldOptions. Should only be used when using the Swift SDK on a secured server ([#370](https://github.com/parse-community/Parse-Swift/pull/370)), thanks to [Corey Baker](https://github.com/cbaker6).
10+
811
### 4.5.0
912
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.4.0...4.5.0)
1013

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
//: [Previous](@previous)
2+
3+
import PlaygroundSupport
4+
import Foundation
5+
import ParseSwift
6+
7+
PlaygroundPage.current.needsIndefiniteExecution = true
8+
initializeParse()
9+
10+
//: Youe specific _User value type.
11+
struct User: ParseUser {
12+
//: These are required by `ParseObject`.
13+
var objectId: String?
14+
var createdAt: Date?
15+
var updatedAt: Date?
16+
var ACL: ParseACL?
17+
var originalData: Data?
18+
19+
//: These are required by `ParseUser`.
20+
var username: String?
21+
var email: String?
22+
var emailVerified: Bool?
23+
var password: String?
24+
var authData: [String: [String: String]?]?
25+
26+
//: Your custom keys.
27+
var customKey: String?
28+
29+
//: Implement your own version of merge
30+
func merge(with object: Self) throws -> Self {
31+
var updated = try mergeParse(with: object)
32+
if updated.shouldRestoreKey(\.customKey,
33+
original: object) {
34+
updated.customKey = object.customKey
35+
}
36+
return updated
37+
}
38+
}
39+
40+
//: Create your own value typed `ParseObject`.
41+
struct GameScore2: ParseObject {
42+
//: These are required by ParseObject
43+
var objectId: String?
44+
var createdAt: Date?
45+
var updatedAt: Date?
46+
var ACL: ParseACL?
47+
var originalData: Data?
48+
49+
//: Your own properties.
50+
var points: Int?
51+
var level: Int?
52+
var data: ParseBytes?
53+
var owner: User?
54+
var rivals: [User]?
55+
56+
//: Implement your own version of merge
57+
func merge(with object: Self) throws -> Self {
58+
var updated = try mergeParse(with: object)
59+
if updated.shouldRestoreKey(\.points,
60+
original: object) {
61+
updated.points = object.points
62+
}
63+
if updated.shouldRestoreKey(\.level,
64+
original: object) {
65+
updated.level = object.level
66+
}
67+
if updated.shouldRestoreKey(\.data,
68+
original: object) {
69+
updated.data = object.data
70+
}
71+
if updated.shouldRestoreKey(\.owner,
72+
original: object) {
73+
updated.owner = object.owner
74+
}
75+
if updated.shouldRestoreKey(\.rivals,
76+
original: object) {
77+
updated.rivals = object.rivals
78+
}
79+
return updated
80+
}
81+
}
82+
83+
//: It's recommended to place custom initializers in an extension
84+
//: to preserve the memberwise initializer.
85+
extension GameScore2 {
86+
87+
init(points: Int) {
88+
self.points = points
89+
}
90+
91+
init(objectId: String?) {
92+
self.objectId = objectId
93+
}
94+
}
95+
96+
//: First lets create a new CLP for the new schema.
97+
let clp = ParseCLP(requiresAuthentication: true, publicAccess: false)
98+
.setAccessPublic(true, on: .get)
99+
.setAccessPublic(true, on: .find)
100+
101+
//: Next we use the CLP to create the new schema and add fields to it.
102+
var gameScoreSchema = ParseSchema<GameScore2>(classLevelPermissions: clp)
103+
.addField("points",
104+
type: .number,
105+
options: ParseFieldOptions<Int>(required: false, defauleValue: nil))
106+
.addField("level",
107+
type: .number,
108+
options: ParseFieldOptions<Int>(required: false, defauleValue: nil))
109+
.addField("data",
110+
type: .bytes,
111+
options: ParseFieldOptions<String>(required: false, defauleValue: nil))
112+
113+
do {
114+
gameScoreSchema = try gameScoreSchema
115+
.addField("owner",
116+
type: .pointer,
117+
options: ParseFieldOptions<User>(required: false, defauleValue: nil))
118+
.addField("rivals",
119+
type: .array,
120+
options: ParseFieldOptions<[User]>(required: false, defauleValue: nil))
121+
} catch {
122+
print("Can't add field: \(gameScoreSchema)")
123+
}
124+
125+
//: Now lets create the schema on the server.
126+
gameScoreSchema.create { result in
127+
switch result {
128+
case .success(let savedSchema):
129+
print("Check GameScore2 in Dashboard. \nThe created schema: \(savedSchema)")
130+
case .failure(let error):
131+
print("Couldn't save schema: \(error)")
132+
}
133+
}
134+
135+
//: We can update the CLP to only allow access to users specified in the "owner" field.
136+
let clp2 = clp.setPointerFields(Set(["owner"]), on: .get)
137+
gameScoreSchema.classLevelPermissions = clp2
138+
139+
//: In addition, we can add an index.
140+
gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "level", index: 1)
141+
142+
//: Next, we need to update the schema on the server with the changes.
143+
gameScoreSchema.update { result in
144+
switch result {
145+
case .success(let updatedSchema):
146+
print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)")
147+
/*:
148+
Updated the current gameScoreSchema with the newest.
149+
*/
150+
gameScoreSchema = updatedSchema
151+
case .failure(let error):
152+
print("Couldn't update schema: \(error)")
153+
}
154+
}
155+
156+
//: Indexes can also be deleted.
157+
gameScoreSchema = gameScoreSchema.deleteIndex("myIndex")
158+
159+
//: Next, we need to update the schema on the server with the changes.
160+
gameScoreSchema.update { result in
161+
switch result {
162+
case .success(let updatedSchema):
163+
print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)")
164+
/*:
165+
Updated the current gameScoreSchema with the newest.
166+
*/
167+
gameScoreSchema = updatedSchema
168+
case .failure(let error):
169+
print("Couldn't update schema: \(error)")
170+
}
171+
}
172+
173+
/*:
174+
Fields can also be deleted on a schema. Lets remove
175+
the **data** field since it's not going being used.
176+
*/
177+
gameScoreSchema = gameScoreSchema.deleteField("data")
178+
179+
//: Next, we need to update the schema on the server with the changes.
180+
gameScoreSchema.update { result in
181+
switch result {
182+
case .success(let updatedSchema):
183+
print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)")
184+
/*:
185+
Updated the current gameScoreSchema with the newest.
186+
*/
187+
gameScoreSchema = updatedSchema
188+
case .failure(let error):
189+
print("Couldn't update schema: \(error)")
190+
}
191+
}
192+
193+
/*:
194+
Sets of fields can also be protected from access. Lets protect
195+
some fields from access.
196+
*/
197+
var clp3 = gameScoreSchema.classLevelPermissions
198+
clp3 = clp3?
199+
.setProtectedFieldsPublic(["owner"])
200+
.setProtectedFields(["level"], userField: "rivals")
201+
gameScoreSchema.classLevelPermissions = clp3
202+
203+
//: Next, we need to update the schema on the server with the changes.
204+
gameScoreSchema.update { result in
205+
switch result {
206+
case .success(let updatedSchema):
207+
print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)")
208+
/*:
209+
Updated the current gameScoreSchema with the newest.
210+
*/
211+
gameScoreSchema = updatedSchema
212+
case .failure(let error):
213+
print("Couldn't update schema: \(error)")
214+
}
215+
}
216+
217+
//: Now lets save a new object to the new schema.
218+
var gameScore = GameScore2()
219+
gameScore.points = 120
220+
gameScore.owner = User.current
221+
222+
gameScore.save { result in
223+
switch result {
224+
case .success(let savedGameScore):
225+
print("The saved GameScore is: \(savedGameScore)")
226+
case .failure(let error):
227+
print("Couldn't save schema: \(error)")
228+
}
229+
}
230+
231+
//: You can delete all objects your schema by purging them.
232+
gameScoreSchema.purge { result in
233+
switch result {
234+
case .success:
235+
print("All objects have been purged from this schema.")
236+
case .failure(let error):
237+
print("Couldn't purge schema: \(error)")
238+
}
239+
}
240+
241+
/*:
242+
As long as there's no data in your `ParseSchema` you can
243+
delete the schema.
244+
*/
245+
gameScoreSchema.delete { result in
246+
switch result {
247+
case .success:
248+
print("The schema has been deleted.")
249+
case .failure(let error):
250+
print("Couldn't delete the schema: \(error)")
251+
}
252+
}
253+
254+
//: [Next](@next)

0 commit comments

Comments
 (0)