Skip to content
GitLab
Explore
Projects
Groups
Snippets
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Memri
iOS application
Commits
0ec7848b
Commit
0ec7848b
authored
4 years ago
by
Ruben Daniels
Browse files
Options
Download
Email Patches
Plain Diff
Temp reverted realm and removed ThreadSafeRefs that werent so safe afterall
parent
ccc741c6
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
memri.xcodeproj/project.pbxproj
+1
-1
memri.xcodeproj/project.pbxproj
memri.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+4
-4
...project.xcworkspace/xcshareddata/swiftpm/Package.resolved
memri/api/Datasource.swift
+19
-50
memri/api/Datasource.swift
memri/cvu/views/CascadableDict.swift
+0
-14
memri/cvu/views/CascadableDict.swift
memri/gui/renderers/MapRenderer/MapHelper.swift
+4
-2
memri/gui/renderers/MapRenderer/MapHelper.swift
memri/model/Cache.swift
+4
-4
memri/model/Cache.swift
memri/model/DatabaseController.swift
+37
-3
memri/model/DatabaseController.swift
memri/model/Sync.swift
+16
-10
memri/model/Sync.swift
memri/model/items/Item.swift
+54
-52
memri/model/items/Item.swift
with
139 additions
and
140 deletions
+139
-140
memri.xcodeproj/project.pbxproj
+
1
-
1
View file @
0ec7848b
...
...
@@ -2258,7 +2258,7 @@
repositoryURL
=
"https://github.com/realm/realm-cocoa"
;
requirement
=
{
kind
=
upToNextMajorVersion
;
minimumVersion
=
5
.0.0
;
minimumVersion
=
4
.0.0
;
};
};
B8FB4994249F21A6007A05C4
/* XCRemoteSwiftPackageReference "ascollectionview" */
=
{
...
...
This diff is collapsed.
Click to expand it.
memri.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+
4
-
4
View file @
0ec7848b
...
...
@@ -42,8 +42,8 @@
"repositoryURL": "https://github.com/realm/realm-cocoa",
"state": {
"branch": null,
"revision": "
ef337f0991255d19e3a14f6c0d718a9f53a55d9
a",
"version": "
5.3
.1"
"revision": "
fa43b8e2909334c79f233ce472332c136ca108d
a",
"version": "
4.4
.1"
}
},
{
...
...
@@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/realm/realm-core",
"state": {
"branch": null,
"revision": "
430bc95a34dd0a72a5ebbdc87d4035f0b43096ae
",
"version": "
6.0.11
"
"revision": "
35662ff940e340bf630ad1d1d88acfc7af18bee6
",
"version": "
5.23.8
"
}
}
]
...
...
This diff is collapsed.
Click to expand it.
memri/api/Datasource.swift
+
19
-
50
View file @
0ec7848b
...
...
@@ -14,26 +14,25 @@ protocol UniqueString {
var
uniqueString
:
String
{
get
}
}
public
class
Datasource
:
SchemaItem
,
UniqueString
{
/// Primary key used in the realm database of this Item
override
public
static
func
primaryKey
()
->
String
?
{
"uid"
}
public
class
Datasource
:
Equatable
,
UniqueString
{
public
static
func
==
(
lhs
:
Datasource
,
rhs
:
Datasource
)
->
Bool
{
lhs
.
uniqueString
==
rhs
.
uniqueString
}
/// Retrieves the query which is used to load data from the pod
@objc
dynamic
var
query
:
String
?
var
query
:
String
?
/// Retrieves the property that is used to sort on
@objc
dynamic
var
sortProperty
:
String
?
var
sortProperty
:
String
?
/// Retrieves whether the sort direction
/// - false sort descending
/// - true sort ascending
let
sortAscending
=
RealmOptional
<
Bool
>
()
var
sortAscending
:
Bool
?
=
nil
/// Retrieves the number of items per page
let
pageCount
=
RealmOptional
<
Int
>
()
// Todo move to ResultSet
var
pageCount
:
Int
?
=
nil
let
pageIndex
=
RealmOptional
<
Int
>
()
// Todo move to ResultSet
var
pageIndex
:
Int
?
=
nil
/// Returns a string representation of the data in QueryOptions that is unique for that data
/// Each QueryOptions object with the same data will return the same uniqueString
var
uniqueString
:
String
{
...
...
@@ -42,46 +41,16 @@ public class Datasource: SchemaItem, UniqueString {
result
.
append
((
query
??
""
)
.
sha256
())
result
.
append
(
sortProperty
??
""
)
let
sortAsc
=
sortAscending
.
value
??
true
let
sortAsc
=
sortAscending
??
true
result
.
append
(
String
(
sortAsc
))
return
result
.
joined
(
separator
:
":"
)
}
convenience
init
(
query
:
String
)
{
self
.
init
()
init
(
query
:
String
?,
sortProperty
:
String
?
=
nil
,
sortAscending
:
Bool
?
=
nil
)
{
self
.
query
=
query
}
required
init
()
{
super
.
init
()
}
required
init
(
from
decoder
:
Decoder
)
throws
{
fatalError
(
"init(from:) has not been implemented"
)
}
public
class
func
fromCVUDefinition
(
_
def
:
CVUParsedDatasourceDefinition
,
_
viewArguments
:
ViewArguments
?
=
nil
)
throws
->
Datasource
{
func
getValue
<
T
>
(
_
name
:
String
)
throws
->
T
?
{
if
let
expr
=
def
[
name
]
as?
Expression
{
do
{
let
x
=
try
expr
.
execForReturnType
(
T
.
self
,
args
:
viewArguments
)
return
x
}
catch
{
debugHistory
.
warn
(
"
\(
error
)
"
)
return
nil
}
}
return
def
[
name
]
as?
T
}
return
try
Cache
.
createItem
(
Datasource
.
self
,
values
:
[
"selector"
:
def
.
selector
??
"[datasource]"
,
"query"
:
try
getValue
(
"query"
)
??
""
,
"sortProperty"
:
try
getValue
(
"sortProperty"
)
??
""
,
"sortAscending"
:
try
getValue
(
"sortAscending"
)
??
true
,
])
self
.
sortProperty
=
sortProperty
self
.
sortAscending
=
sortAscending
}
}
...
...
@@ -121,11 +90,11 @@ public class CascadingDatasource: Cascadable, UniqueString, Subscriptable {
}
func
flattened
()
->
Datasource
{
Datasource
(
value
:
[
"
query
"
:
query
as
Any
,
"
sortProperty
"
:
sortProperty
as
Any
,
"
sortAscending
"
:
sortAscending
as
Any
,
]
)
Datasource
(
query
:
query
,
sortProperty
:
sortProperty
,
sortAscending
:
sortAscending
)
}
//
// init(_ cascadeStack: [CVUParsedDatasourceDefinition],
...
...
This diff is collapsed.
Click to expand it.
memri/cvu/views/CascadableDict.swift
+
0
-
14
View file @
0ec7848b
...
...
@@ -8,20 +8,6 @@ import Foundation
import
SwiftUI
import
RealmSwift
class
ItemReference
{
let
uid
:
Int
let
type
:
Item
.
Type
init
(
to
:
Item
)
{
uid
=
to
.
uid
.
value
??
-
1
type
=
to
.
getType
()
??
Item
.
self
}
func
resolve
()
->
Item
?
{
DatabaseController
.
read
{
$0
.
object
(
ofType
:
type
,
forPrimaryKey
:
uid
)
}
}
}
public
class
CascadableDict
:
Cascadable
,
CustomStringConvertible
,
Subscriptable
{
func
get
<
T
>
(
_
name
:
String
,
type
:
T
.
Type
=
T
.
self
)
->
T
?
{
guard
let
value
=
cascadeProperty
(
name
,
type
:
Any
?
.
self
)
else
{
...
...
This diff is collapsed.
Click to expand it.
memri/gui/renderers/MapRenderer/MapHelper.swift
+
4
-
2
View file @
0ec7848b
...
...
@@ -85,8 +85,10 @@ class MapHelper {
lookup
.
sink
{
[
weak
self
]
location
in
if
let
location
=
location
{
// Update the address with the location (avoid future lookups)
let
addressReference
=
ThreadSafeReference
(
to
:
address
)
DatabaseController
.
writeAsync
(
withResolvedReferenceTo
:
addressReference
)
{
_
,
address
in
let
safeRef
=
ItemReference
(
to
:
address
)
DatabaseController
.
writeAsync
{
_
in
guard
let
address
=
safeRef
.
resolve
()
as?
Address
else
{
return
}
let
newLocation
=
try
Cache
.
createItem
(
Location
.
self
,
values
:
[
"latitude"
:
location
.
coordinate
.
latitude
,
"longitude"
:
location
.
coordinate
.
longitude
...
...
This diff is collapsed.
Click to expand it.
memri/model/Cache.swift
+
4
-
4
View file @
0ec7848b
...
...
@@ -202,7 +202,7 @@ public class Cache {
if
let
sortProperty
=
datasource
.
sortProperty
,
sortProperty
!=
""
{
result
=
result
.
sorted
(
byKeyPath
:
sortProperty
,
ascending
:
datasource
.
sortAscending
.
value
??
true
ascending
:
datasource
.
sortAscending
??
true
)
}
...
...
@@ -254,7 +254,7 @@ public class Cache {
// Make sure the new resultset has the right query properties
resultSet
.
datasource
.
query
=
datasource
.
query
resultSet
.
datasource
.
sortProperty
=
datasource
.
sortProperty
resultSet
.
datasource
.
sortAscending
.
value
=
datasource
.
sortAscending
.
value
resultSet
.
datasource
.
sortAscending
=
datasource
.
sortAscending
// Make sure the UI updates when the resultset updates
cancellables
.
append
(
resultSet
.
objectWillChange
.
sink
{
_
in
...
...
@@ -324,7 +324,7 @@ public class Cache {
private
func
bindChangeListeners
(
_
item
:
Item
)
{
// Update the sync state when the item changes
rlmTokens
.
append
(
item
.
observe
{
objectChange
in
if
case
let
.
change
(
_
,
propChanges
)
=
objectChange
{
if
case
let
.
change
(
propChanges
)
=
objectChange
{
if
item
.
_action
==
nil
{
func
doAction
()
{
// Mark item for updating
...
...
@@ -522,7 +522,7 @@ public class Cache {
dict
[
"uid"
]
=
try
Cache
.
incrementUID
()
}
print
(
"
\(
type
)
-
\(
dict
[
"uid"
]
)
"
)
//
print("\(type) - \(dict["uid"])")
item
=
realm
.
create
(
type
,
value
:
dict
)
...
...
This diff is collapsed.
Click to expand it.
memri/model/DatabaseController.swift
+
37
-
3
View file @
0ec7848b
...
...
@@ -9,6 +9,40 @@
import
Foundation
import
RealmSwift
class
ItemReference
{
let
uid
:
Int
let
type
:
Item
.
Type
init
(
to
:
Item
)
{
uid
=
to
.
uid
.
value
??
-
1
type
=
to
.
getType
()
??
Item
.
self
}
func
resolve
()
->
Item
?
{
DatabaseController
.
read
{
$0
.
object
(
ofType
:
type
,
forPrimaryKey
:
uid
)
}
}
}
class
EdgeReference
{
let
type
:
String
let
sourceItemID
:
Int
let
targetItemID
:
Int
init
(
to
:
Edge
)
{
type
=
to
.
type
??
""
sourceItemID
=
to
.
sourceItemID
.
value
??
-
1
targetItemID
=
to
.
targetItemID
.
value
??
-
1
}
func
resolve
()
->
Edge
?
{
DatabaseController
.
read
{
$0
.
objects
(
Edge
.
self
)
.
filter
(
"type = '
\(
type
)
' AND sourceItemID =
\(
sourceItemID
)
AND targetItemID =
\(
targetItemID
)
"
)
.
first
}
}
}
class
DatabaseController
{
private
init
()
{}
...
...
@@ -30,7 +64,7 @@ class DatabaseController {
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
}
}
)
}
...
...
@@ -99,7 +133,6 @@ class DatabaseController {
}
}
static
func
read
<
T
>
(
_
doRead
:
((
Realm
)
throws
->
T
?)
)
->
T
?
{
do
{
return
try
tryRead
(
doRead
)
...
...
@@ -177,7 +210,8 @@ class DatabaseController {
}
/// Use this for writing to Realm in the background. It will run on a background thread.
static
func
writeAsync
<
T
>
(
withResolvedReferenceTo
objectReference
:
ThreadSafeReference
<
T
>
,
_
doWrite
:
@escaping
(
Realm
,
T
)
throws
->
Void
)
{
static
func
writeAsync
<
T
>
(
withResolvedReferenceTo
objectReference
:
ThreadSafeReference
<
T
>
,
_
doWrite
:
@escaping
(
Realm
,
T
)
throws
->
Void
)
{
realmQueue
.
async
{
autoreleasepool
{
do
{
...
...
This diff is collapsed.
Click to expand it.
memri/model/Sync.swift
+
16
-
10
View file @
0ec7848b
...
...
@@ -78,7 +78,7 @@ class Sync {
let
data
=
try
MemriJSONEncoder
.
encode
([
// TODO: move this to Datasource
"query"
:
datasource
.
query
,
"sortProperty"
:
datasource
.
sortProperty
,
"sortAscending"
:
datasource
.
sortAscending
.
value
??
false
?
"true"
:
"false"
,
"sortAscending"
:
datasource
.
sortAscending
??
false
?
"true"
:
"false"
,
]
as?
[
String
:
String
])
// Add to realm
...
...
@@ -134,7 +134,7 @@ class Sync {
debugHistory
.
info
(
"Syncing from pod with query:
\(
datasource
.
query
??
""
)
"
)
let
wrappedObject
=
ThreadSafe
Reference
(
to
:
audititem
)
let
safeRef
=
Item
Reference
(
to
:
audititem
)
// Call out to the pod with the query
podAPI
.
query
(
datasource
)
{
error
,
items
in
...
...
@@ -163,8 +163,8 @@ class Sync {
}
// We no longer need to process this log item
DatabaseController
.
writeAsync
(
withResolvedReferenceTo
:
wrappedObject
)
{
_
,
audititem
in
audititem
.
_action
=
nil
DatabaseController
.
writeAsync
{
_
in
safeRef
.
resolve
()?
.
_action
=
nil
}
}
}
else
{
...
...
@@ -205,7 +205,10 @@ class Sync {
DatabaseController
.
writeAsync
{
realm
in
for
(
_
,
sublist
)
in
list
{
for
item
in
sublist
as?
[
Any
]
??
[]
{
if
let
item
=
item
as?
ThreadSafeReference
<
SchemaItem
>
,
let
resolvedItem
=
realm
.
resolve
(
item
)
{
if
let
item
=
item
as?
ItemReference
,
let
resolvedItem
=
item
.
resolve
()
{
if
resolvedItem
.
_action
==
"delete"
{
realm
.
delete
(
resolvedItem
)
}
...
...
@@ -213,7 +216,10 @@ class Sync {
resolvedItem
.
_action
=
""
resolvedItem
.
_updated
.
removeAll
()
}
}
else
if
let
item
=
item
as?
ThreadSafeReference
<
Edge
>
,
let
resolvedItem
=
realm
.
resolve
(
item
)
{
}
else
if
let
item
=
item
as?
EdgeReference
,
let
resolvedItem
=
item
.
resolve
()
{
if
resolvedItem
.
_action
==
"delete"
{
realm
.
delete
(
resolvedItem
)
}
...
...
@@ -232,14 +238,14 @@ class Sync {
DatabaseController
.
read
{
realm
in
var
found
=
0
var
itemQueue
:
[
String
:
[
Schema
Item
]]
=
[
"create"
:
[],
"update"
:
[],
"delete"
:
[]]
var
itemQueue
:
[
String
:
[
Item
]]
=
[
"create"
:
[],
"update"
:
[],
"delete"
:
[]]
var
edgeQueue
:
[
String
:
[
Edge
]]
=
[
"create"
:
[],
"update"
:
[],
"delete"
:
[]]
// Items
for
itemType
in
ItemFamily
.
allCases
{
if
itemType
==
.
typeUserState
{
continue
}
if
let
type
=
itemType
.
getType
()
as?
Schema
Item
.
Type
{
if
let
type
=
itemType
.
getType
()
as?
Item
.
Type
{
let
items
=
realm
.
objects
(
type
)
.
filter
(
"_action != nil"
)
for
item
in
items
{
if
let
action
=
item
.
_action
,
itemQueue
[
action
]
!=
nil
{
...
...
@@ -260,11 +266,11 @@ class Sync {
}
let
safeItemQueue
=
itemQueue
.
mapValues
{
$0
.
map
{
ThreadSafe
Reference
(
to
:
$0
)
}
$0
.
map
{
Item
Reference
(
to
:
$0
)
}
}
let
safeEdgeQueue
=
edgeQueue
.
mapValues
{
$0
.
map
{
ThreadSaf
eReference
(
to
:
$0
)
}
$0
.
map
{
Edg
eReference
(
to
:
$0
)
}
}
if
found
>
0
{
...
...
This diff is collapsed.
Click to expand it.
memri/model/items/Item.swift
+
54
-
52
View file @
0ec7848b
...
...
@@ -616,62 +616,64 @@ public class Item: SchemaItem {
/// update the dateAccessed property to the current date
public
func
accessed
()
{
#warning("REPLACE ME with ItemReference - we don't know what thread this Item was made on... so even `self` may not be safe")
// let safeSelf = ThreadSafeReference(to: self)
// DatabaseController.writeAsync(withResolvedReferenceTo: safeSelf) { realm, item in
// item.dateAccessed = Date()
//
// let auditItem = try Cache.createItem(AuditItem.self, values: ["action": "read"])
// _ = try item.link(auditItem, type: "changelog")
// }
let
safeSelf
=
ItemReference
(
to
:
self
)
DatabaseController
.
writeAsync
{
realm
in
guard
let
item
=
safeSelf
.
resolve
()
else
{
return
}
item
.
dateAccessed
=
Date
()
let
auditItem
=
try
Cache
.
createItem
(
AuditItem
.
self
,
values
:
[
"action"
:
"read"
])
_
=
try
item
.
link
(
auditItem
,
type
:
"changelog"
)
}
}
/// update the dateAccessed property to the current date
public
func
modified
(
_
updatedFields
:[
String
])
{
#warning("REPLACE ME with ItemReference - we don't know what thread this Item was made on... so even `self` may not be safe")
// let safeSelf = ThreadSafeReference(to: self)
// DatabaseController.writeAsync(withResolvedReferenceTo: safeSelf) { realm, item in
// let previousModified = item.dateModified
// item.dateModified = Date()
//
// for field in updatedFields {
// if !item._updated.contains(field) {
// item._updated.append(field)
// }
// }
//
// if previousModified?.distance(to: Date()) ?? 0 < 300 /* 5 minutes */ {
// #warning("Test that .last gives the last added audit item")
// if
// let auditItem = item.edges("changelog")?.last?.item(type: AuditItem.self),
// let content = auditItem.content,
// var dict = try unserialize(content, type: [String:AnyCodable?].self)
// {
// for field in updatedFields {
// guard item.objectSchema[field] != nil else { throw "Invalid update call" }
// dict[field] = AnyCodable(item[field])
// }
// auditItem.content = String(data: try MemriJSONEncoder.encode(dict), encoding: .utf8) ?? ""
// return
// }
// }
//
// var dict = [String:AnyCodable?]()
// for field in updatedFields {
// guard item.objectSchema[field] != nil else { throw "Invalid update call" }
// dict[field] = AnyCodable(item[field])
// }
//
// let content = String(data: try MemriJSONEncoder.encode(dict), encoding: .utf8) ?? ""
// let auditItem = try Cache.createItem(
// AuditItem.self,
// values: [
// "action": "update",
// "content": content
// ]
// )
// _ = try item.link(auditItem, type: "changelog")
// }
let
safeSelf
=
ItemReference
(
to
:
self
)
DatabaseController
.
writeAsync
{
realm
in
guard
let
item
=
safeSelf
.
resolve
()
else
{
return
}
let
previousModified
=
item
.
dateModified
item
.
dateModified
=
Date
()
for
field
in
updatedFields
{
if
!
item
.
_updated
.
contains
(
field
)
{
item
.
_updated
.
append
(
field
)
}
}
if
previousModified
?
.
distance
(
to
:
Date
())
??
0
<
300
/* 5 minutes */
{
#warning("Test that .last gives the last added audit item")
if
let
auditItem
=
item
.
edges
(
"changelog"
)?
.
last
?
.
item
(
type
:
AuditItem
.
self
),
let
content
=
auditItem
.
content
,
var
dict
=
try
unserialize
(
content
,
type
:
[
String
:
AnyCodable
?]
.
self
)
{
for
field
in
updatedFields
{
guard
item
.
objectSchema
[
field
]
!=
nil
else
{
throw
"Invalid update call"
}
dict
[
field
]
=
AnyCodable
(
item
[
field
])
}
auditItem
.
content
=
String
(
data
:
try
MemriJSONEncoder
.
encode
(
dict
),
encoding
:
.
utf8
)
??
""
return
}
}
var
dict
=
[
String
:
AnyCodable
?]()
for
field
in
updatedFields
{
guard
item
.
objectSchema
[
field
]
!=
nil
else
{
throw
"Invalid update call"
}
dict
[
field
]
=
AnyCodable
(
item
[
field
])
}
let
content
=
String
(
data
:
try
MemriJSONEncoder
.
encode
(
dict
),
encoding
:
.
utf8
)
??
""
let
auditItem
=
try
Cache
.
createItem
(
AuditItem
.
self
,
values
:
[
"action"
:
"update"
,
"content"
:
content
]
)
_
=
try
item
.
link
(
auditItem
,
type
:
"changelog"
)
}
}
/// compare two dataItems
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Snippets