Compare commits
570 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dda69389eb | ||
|
|
b20be10a19 | ||
|
|
0ee8fbabd5 | ||
|
|
1f562b25a7 | ||
|
|
593c0510d3 | ||
|
|
338e4ddc9f | ||
|
|
bfb1df3c40 | ||
|
|
003bf897e2 | ||
|
|
0b127956d3 | ||
|
|
098e560fcc | ||
|
|
7dbd3777ec | ||
|
|
447d8e5880 | ||
|
|
1f97225efa | ||
|
|
d13b0cfabb | ||
|
|
d7b852fca4 | ||
|
|
f2efe175e5 | ||
|
|
8f3a6638d8 | ||
|
|
f7471f56a4 | ||
|
|
edd8ba55d8 | ||
|
|
18aac84335 | ||
|
|
c6be892cb0 | ||
|
|
2cd8101987 | ||
|
|
e1aed37da0 | ||
|
|
5de5ecee06 | ||
|
|
9406901b28 | ||
|
|
477f478d85 | ||
|
|
668b5ad606 | ||
|
|
f985828f3b | ||
|
|
bb3bc940c2 | ||
|
|
2d5bc77219 | ||
|
|
a40df37192 | ||
|
|
63b3d25d78 | ||
|
|
74721b5c12 | ||
|
|
f136549b46 | ||
|
|
4ec2b2e311 | ||
|
|
3d82ee18c7 | ||
|
|
d6eae8c091 | ||
|
|
6f8d86cd8f | ||
|
|
3b6f226389 | ||
|
|
228e4e8e1a | ||
|
|
98a42feb95 | ||
|
|
2f93ee7591 | ||
|
|
11f5cb9a05 | ||
|
|
2c00fc31bc | ||
|
|
2bbf6b34ea | ||
|
|
8d7f282743 | ||
|
|
007da014f8 | ||
|
|
b26e50f777 | ||
|
|
611bc53c9a | ||
|
|
9d36582c10 | ||
|
|
1c735a9228 | ||
|
|
1db91fcec3 | ||
|
|
8b3b947406 | ||
|
|
2c0cadf2fa | ||
|
|
5e536556da | ||
|
|
d75029f54b | ||
|
|
204c4de1f6 | ||
|
|
0f3455a4a4 | ||
|
|
72f36e7237 | ||
|
|
d988daa025 | ||
|
|
e720504855 | ||
|
|
ee51c5ad9c | ||
|
|
4ac7df7364 | ||
|
|
56f873ccb3 | ||
|
|
1f90f846f3 | ||
|
|
4a97d5a8dc | ||
|
|
0eb9b6393d | ||
|
|
56d0ea46ea | ||
|
|
a7568eebdb | ||
|
|
4049e1944a | ||
|
|
73b7fcd907 | ||
|
|
74c1a97af4 | ||
|
|
97f2a53124 | ||
|
|
b6db872be0 | ||
|
|
0d9299f900 | ||
|
|
7f928dc684 | ||
|
|
231e138ab0 | ||
|
|
361dba58c6 | ||
|
|
e1b03b4a89 | ||
|
|
0df6c737c1 | ||
|
|
12c5aeaaa4 | ||
|
|
dd3fb17dd0 | ||
|
|
627a5d4355 | ||
|
|
58629bc1df | ||
|
|
f925803b93 | ||
|
|
843adf21f7 | ||
|
|
2d1b1e0592 | ||
|
|
38e9878c04 | ||
|
|
8cb8b95c2e | ||
|
|
8e4e308ccc | ||
|
|
f0f4049798 | ||
|
|
c20fe4ac17 | ||
|
|
e9c3312612 | ||
|
|
92ad895044 | ||
|
|
bcc2d9def3 | ||
|
|
43f61359da | ||
|
|
5e37ee4566 | ||
|
|
c544e0cce8 | ||
|
|
f119a3adec | ||
|
|
c951cb87a3 | ||
|
|
08147806a0 | ||
|
|
4beb11519e | ||
|
|
b7ebda4487 | ||
|
|
b4489301ac | ||
|
|
c025e5acc6 | ||
|
|
57745f36a8 | ||
|
|
eef1c99f11 | ||
|
|
9a19919392 | ||
|
|
3e2d62fe67 | ||
|
|
6f275eb63a | ||
|
|
b12dba4d15 | ||
|
|
4ee1b04523 | ||
|
|
b1decc9853 | ||
|
|
c2e4c033ef | ||
|
|
e12223df85 | ||
|
|
468922d5ed | ||
|
|
6b9a4b480b | ||
|
|
81b482e28b | ||
|
|
c112a84c0a | ||
|
|
88ab0b5e15 | ||
|
|
717cb75720 | ||
|
|
998938490c | ||
|
|
f3beca8769 | ||
|
|
4baeb6d922 | ||
|
|
98d860aff6 | ||
|
|
11a9e3991c | ||
|
|
f380d9dc25 | ||
|
|
d546ff154f | ||
|
|
f21597d332 | ||
|
|
d971c3a2ac | ||
|
|
80166a42bb | ||
|
|
3d8bdf1cf3 | ||
|
|
1ddce5aa8d | ||
|
|
7a1600fac4 | ||
|
|
e4e664d8ce | ||
|
|
145edd5a7d | ||
|
|
f5fed063ee | ||
|
|
a267395618 | ||
|
|
326b897b06 | ||
|
|
0b18366ab1 | ||
|
|
ddf599ba85 | ||
|
|
6e3e540d0a | ||
|
|
bd066f0cef | ||
|
|
9764f33086 | ||
|
|
0c19c878c5 | ||
|
|
1b8e517b5a | ||
|
|
2818a778a4 | ||
|
|
64a0264354 | ||
|
|
7932625644 | ||
|
|
4619fbbec3 | ||
|
|
6b64eb7650 | ||
|
|
f5a165d47d | ||
|
|
12c58e3955 | ||
|
|
5af0d17de4 | ||
|
|
81dfb8e3e5 | ||
|
|
d5114fc4bc | ||
|
|
693fc7fbbb | ||
|
|
b073b7e795 | ||
|
|
56d9719984 | ||
|
|
953c9723a8 | ||
|
|
c5a996d5ed | ||
|
|
53a83d823e | ||
|
|
6d75dcbc32 | ||
|
|
0345ee9c94 | ||
|
|
4016b5dc83 | ||
|
|
64b5e102aa | ||
|
|
8e3c44c072 | ||
|
|
12dc32f7e6 | ||
|
|
b4c38caf1e | ||
|
|
266b1a9913 | ||
|
|
1740d168c8 | ||
|
|
d42b397090 | ||
|
|
058cc1915b | ||
|
|
02d5bf85ae | ||
|
|
f0dac2454c | ||
|
|
d4deed0cf7 | ||
|
|
79655ffde5 | ||
|
|
cf46b45e8e | ||
|
|
ed3d21db77 | ||
|
|
f16e3593c0 | ||
|
|
67bb9340c7 | ||
|
|
5d49bd79f2 | ||
|
|
9f397b4337 | ||
|
|
7508d150d6 | ||
|
|
239db8168d | ||
|
|
30ab4386bf | ||
|
|
08053ccb15 | ||
|
|
367cfea8a7 | ||
|
|
5d3b7e3dab | ||
|
|
fe7e6e7b84 | ||
|
|
b2dba0d6fd | ||
|
|
6f290213fa | ||
|
|
0ab52d2f43 | ||
|
|
bf8a1062e0 | ||
|
|
0e254867b6 | ||
|
|
0376ac6908 | ||
|
|
b3421888a6 | ||
|
|
7b3f4ae0a4 | ||
|
|
b4e12cc922 | ||
|
|
6c282b18af | ||
|
|
ebbde8b7b6 | ||
|
|
a1407e4121 | ||
|
|
99b871b97a | ||
|
|
1cd3c4fcf4 | ||
|
|
e09ac9ee00 | ||
|
|
4b0d134acb | ||
|
|
eeec3979ee | ||
|
|
5b365c642d | ||
|
|
635201868a | ||
|
|
db4426e6b9 | ||
|
|
41902fee2e | ||
|
|
9239370793 | ||
|
|
a4d2f326a5 | ||
|
|
9f54ec26e2 | ||
|
|
9425cb56d7 | ||
|
|
1de56d2776 | ||
|
|
b3261ea930 | ||
|
|
88dd7aef72 | ||
|
|
2863605d84 | ||
|
|
94d9116299 | ||
|
|
57ddbbd515 | ||
|
|
78954b9d78 | ||
|
|
4eb06c9858 | ||
|
|
0d634c1dcc | ||
|
|
84f3740ea1 | ||
|
|
6dc48b6af7 | ||
|
|
09ce2816bf | ||
|
|
62e962eebe | ||
|
|
682472c1bd | ||
|
|
46ab70b839 | ||
|
|
ac8304f977 | ||
|
|
5777831565 | ||
|
|
42d1f41939 | ||
|
|
8c30ec3a3d | ||
|
|
237a1b648e | ||
|
|
614f1572c2 | ||
|
|
a48f16aa8c | ||
|
|
cdbadae002 | ||
|
|
4a28a39df6 | ||
|
|
5febf2542d | ||
|
|
c21ab11a41 | ||
|
|
10cd18dbf0 | ||
|
|
8409a13289 | ||
|
|
a9a73fa5c4 | ||
|
|
82cae2e11e | ||
|
|
42caee2418 | ||
|
|
1dea1d0d06 | ||
|
|
d344b9d878 | ||
|
|
95c1ce52cc | ||
|
|
cc346816d6 | ||
|
|
f14b561c33 | ||
|
|
6f655951aa | ||
|
|
ff8fbae568 | ||
|
|
4d4b02d076 | ||
|
|
06c0981ded | ||
|
|
20d581d536 | ||
|
|
45e110755d | ||
|
|
ab40532801 | ||
|
|
40f458a09c | ||
|
|
1ad233ca9d | ||
|
|
3a8e394272 | ||
|
|
1f54daa528 | ||
|
|
f25879b6fe | ||
|
|
8fa3109e91 | ||
|
|
808e8ff97a | ||
|
|
30685d4355 | ||
|
|
7152962026 | ||
|
|
91735e38d1 | ||
|
|
333a50ad9e | ||
|
|
b7a602fc67 | ||
|
|
2ff7ecef96 | ||
|
|
e3f9139304 | ||
|
|
4aa461872f | ||
|
|
3d43314076 | ||
|
|
7b1075b759 | ||
|
|
0c29e07ddb | ||
|
|
ce91cf22f9 | ||
|
|
2dd033c002 | ||
|
|
c7fcba112e | ||
|
|
c8b1c4358f | ||
|
|
5431e2e974 | ||
|
|
83e6082c56 | ||
|
|
c7bdbbde57 | ||
|
|
abf15c8aa8 | ||
|
|
d9db7e4a82 | ||
|
|
65dbc22ddd | ||
|
|
51961dd737 | ||
|
|
18a7055cdf | ||
|
|
4b9fddad6a | ||
|
|
fbe7bd7bf8 | ||
|
|
15edabdbb5 | ||
|
|
f447bcfb95 | ||
|
|
15e5e4fdf6 | ||
|
|
583c6b7249 | ||
|
|
18b933957e | ||
|
|
2c7039232e | ||
|
|
d3b3b5ff4a | ||
|
|
d90e8d1303 | ||
|
|
b55dd13dff | ||
|
|
66dd5b6f27 | ||
|
|
49c4b770eb | ||
|
|
662aaa1e75 | ||
|
|
dd4e47d7f9 | ||
|
|
9ea5073bc8 | ||
|
|
e314db8f56 | ||
|
|
48d936d068 | ||
|
|
b2ff8a15ef | ||
|
|
8a4d1cd7c6 | ||
|
|
8ce26c213d | ||
|
|
f3816b9abf | ||
|
|
21961780d4 | ||
|
|
305e2b61a0 | ||
|
|
0430f66240 | ||
|
|
65772edd00 | ||
|
|
02d7870d75 | ||
|
|
aca1709e13 | ||
|
|
b6ee0b014f | ||
|
|
e37186da73 | ||
|
|
588fa35c84 | ||
|
|
f6614cda66 | ||
|
|
639574d8c2 | ||
|
|
094703155c | ||
|
|
6dd254e713 | ||
|
|
204025721c | ||
|
|
27ffc1d225 | ||
|
|
ba6f0c39d5 | ||
|
|
ab2eac8f6c | ||
|
|
f460a0b30f | ||
|
|
50bc3ace06 | ||
|
|
d2ddf2002f | ||
|
|
b4117eeb02 | ||
|
|
106275b2dd | ||
|
|
08d9298be0 | ||
|
|
ff0c4d94fc | ||
|
|
50e50c0613 | ||
|
|
5c8a0e425b | ||
|
|
03b71caf7e | ||
|
|
7ff29d6086 | ||
|
|
8d86425875 | ||
|
|
97242d9726 | ||
|
|
780ff4e60b | ||
|
|
11743dfb5f | ||
|
|
9eaf85388c | ||
|
|
06635c9d2f | ||
|
|
1d2eef7894 | ||
|
|
85ed815ec2 | ||
|
|
096e5493a6 | ||
|
|
0aa8c03424 | ||
|
|
4ead3c34dd | ||
|
|
645034dde5 | ||
|
|
85706a3c57 | ||
|
|
c5ae4606b9 | ||
|
|
fa682215c5 | ||
|
|
e814733ae9 | ||
|
|
e2236698fa | ||
|
|
be5da632b3 | ||
|
|
dc73cd6dd9 | ||
|
|
7beb3bec75 | ||
|
|
53100b202d | ||
|
|
cc84b1f8bd | ||
|
|
474f52ed2b | ||
|
|
16225fc4c6 | ||
|
|
03bb7619da | ||
|
|
1bfb7451c3 | ||
|
|
3e082d5ed4 | ||
|
|
e45d67252c | ||
|
|
3d427c29c4 | ||
|
|
1068517b94 | ||
|
|
0d23ce1598 | ||
|
|
fd1ce20863 | ||
|
|
2c5fa63f40 | ||
|
|
78e43a37a5 | ||
|
|
fde85a9743 | ||
|
|
dbbc0adae5 | ||
|
|
2a62770552 | ||
|
|
f436b26e8e | ||
|
|
c566226747 | ||
|
|
cd405e038e | ||
|
|
92a37053b0 | ||
|
|
0b57cff27d | ||
|
|
3ebc44b546 | ||
|
|
68f1027ba7 | ||
|
|
005729be85 | ||
|
|
8bac4aa901 | ||
|
|
da170c7e51 | ||
|
|
211e69023e | ||
|
|
2912dcf010 | ||
|
|
3e00a3da06 | ||
|
|
a33e248828 | ||
|
|
75e14fbbed | ||
|
|
9922deac4d | ||
|
|
86be046c9f | ||
|
|
0f10bc3349 | ||
|
|
9685f0aef2 | ||
|
|
1e51000155 | ||
|
|
3bd459bb1a | ||
|
|
c89bc3c227 | ||
|
|
0f405a50aa | ||
|
|
f5e1643ef7 | ||
|
|
f62137fb58 | ||
|
|
37fbedd799 | ||
|
|
28b43f33fa | ||
|
|
81a72f29ea | ||
|
|
f7aaf4fb2a | ||
|
|
a52acf2ebd | ||
|
|
74c64619c3 | ||
|
|
4a5bc6450b | ||
|
|
fe69e7c6c4 | ||
|
|
ccf7c62aad | ||
|
|
f36cb8af63 | ||
|
|
10ccadd96c | ||
|
|
5c0e78bd53 | ||
|
|
8a09688117 | ||
|
|
a366bcf1a3 | ||
|
|
fcd4be9011 | ||
|
|
535eb76adc | ||
|
|
c6e68ac24f | ||
|
|
aff966aac9 | ||
|
|
32743b3aee | ||
|
|
a20ad87583 | ||
|
|
a11915db12 | ||
|
|
961f39a806 | ||
|
|
3096cb784c | ||
|
|
809aa4ff96 | ||
|
|
8d926d25ec | ||
|
|
790454f514 | ||
|
|
9a38707c58 | ||
|
|
fb7e2f7f7f | ||
|
|
f72efc80b2 | ||
|
|
fcda5399da | ||
|
|
fd3a9b00ec | ||
|
|
f56c37f9ee | ||
|
|
5f5000218a | ||
|
|
e8eb309d82 | ||
|
|
d0c3203e63 | ||
|
|
f5b3901caa | ||
|
|
1a99fea820 | ||
|
|
195b60615b | ||
|
|
2c394965b8 | ||
|
|
746d697691 | ||
|
|
5689158b43 | ||
|
|
6fcdf3d011 | ||
|
|
e26573c18e | ||
|
|
801cf8d9f0 | ||
|
|
eced8f2e93 | ||
|
|
3b735d07ec | ||
|
|
5eb5476e3a | ||
|
|
6a42a0054e | ||
|
|
8c437e19b8 | ||
|
|
9cd3b6c879 | ||
|
|
fe135acbec | ||
|
|
997c5bdcfa | ||
|
|
23e12c4539 | ||
|
|
ca9798be14 | ||
|
|
6e01a58c85 | ||
|
|
f618617053 | ||
|
|
49b8b9c372 | ||
|
|
129f975d96 | ||
|
|
a2e463e58c | ||
|
|
5fd50f0e15 | ||
|
|
7f9a915d71 | ||
|
|
0a81736b7a | ||
|
|
f9b6dd0c6a | ||
|
|
0354401b56 | ||
|
|
0304067beb | ||
|
|
ddd83da434 | ||
|
|
fc7df671de | ||
|
|
5fde9030c7 | ||
|
|
d7b07b3f00 | ||
|
|
ddd1ffb29f | ||
|
|
98094000bb | ||
|
|
d5026ef996 | ||
|
|
2cd913b9dd | ||
|
|
ad9520abbc | ||
|
|
c0fc57d10c | ||
|
|
0cf4d303e4 | ||
|
|
6de397958a | ||
|
|
55292a84dc | ||
|
|
981b560d53 | ||
|
|
c4c4dd55cd | ||
|
|
1ddbe20c86 | ||
|
|
da9e8c1550 | ||
|
|
ef0937fec4 | ||
|
|
35885b40de | ||
|
|
d669569196 | ||
|
|
ae919ff3c8 | ||
|
|
1a7a4690d1 | ||
|
|
b9b96d1a35 | ||
|
|
da3a9590ac | ||
|
|
6b3d75bea1 | ||
|
|
d44721fef0 | ||
|
|
3f268e8376 | ||
|
|
303fea4ebe | ||
|
|
77173cdad0 | ||
|
|
1e24a7d739 | ||
|
|
a3b33bedb8 | ||
|
|
eaf7544c50 | ||
|
|
67863120e0 | ||
|
|
1b0e305d9a | ||
|
|
91fda01071 | ||
|
|
feb0e30735 | ||
|
|
9c25336ff6 | ||
|
|
66bef87422 | ||
|
|
dd2949ee18 | ||
|
|
e0abb9b0af | ||
|
|
42c28b064a | ||
|
|
0b097b8c85 | ||
|
|
fce5d2f301 | ||
|
|
8ff163af30 | ||
|
|
19abedfa9f | ||
|
|
48828fdca3 | ||
|
|
9d65a27557 | ||
|
|
6d04806608 | ||
|
|
54129f7362 | ||
|
|
54c81d23f5 | ||
|
|
53ab140341 | ||
|
|
9dc4331b26 | ||
|
|
e6aa72fb5f | ||
|
|
274a54451a | ||
|
|
fe70b7a27d | ||
|
|
02a660e4a6 | ||
|
|
a543a4c94a | ||
|
|
fd14a18248 | ||
|
|
b0e2655bdf | ||
|
|
b6bc7c2edf | ||
|
|
1938f0d9de | ||
|
|
94e6db669f | ||
|
|
b1972b82f1 | ||
|
|
5ffaca1375 | ||
|
|
a73306fecb | ||
|
|
9f3db61ff7 | ||
|
|
e5ef4992d3 | ||
|
|
c0ae129b22 | ||
|
|
4aa1a63f9a | ||
|
|
4fc10afe1e | ||
|
|
8b77e4e5a0 | ||
|
|
258c237100 | ||
|
|
6948db516d | ||
|
|
b5d80fd272 | ||
|
|
cdcd7d0416 | ||
|
|
a5162239d5 | ||
|
|
1ad6ac5769 | ||
|
|
ecb3d0cfa0 | ||
|
|
81355fd9c5 | ||
|
|
97d7a276fe | ||
|
|
d72d1afe8b | ||
|
|
716e069984 | ||
|
|
881ee4af0a | ||
|
|
acc0ce1c32 | ||
|
|
b97f6d6a0a | ||
|
|
818abfc1a1 | ||
|
|
f055c54a66 | ||
|
|
cb6d5b015b | ||
|
|
494965de23 | ||
|
|
fe25a9aa36 | ||
|
|
f21e4e12e0 | ||
|
|
92890d1e1d | ||
|
|
0c483e0e19 | ||
|
|
6055685c00 | ||
|
|
ee71442b08 | ||
|
|
341ec5e771 | ||
|
|
8569c3c524 | ||
|
|
3224fcf71d | ||
|
|
9ff1c9d545 | ||
|
|
c40d17a6ad | ||
|
|
9d5e04854a | ||
|
|
d2fd03c1f0 | ||
|
|
7baaee493d | ||
|
|
03973790a8 | ||
|
|
7ee027f44d |
2
.cocoapods.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
try:
|
||||
project: 'CoreStore.xcworkspace'
|
||||
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [JohnEstropia]
|
||||
7
.gitignore
vendored
@@ -7,3 +7,10 @@ CoreStore.xcworkspace/xcuserdata
|
||||
.DS_Store
|
||||
DerivedData
|
||||
*.orig
|
||||
build
|
||||
Playground_macOS.playground/playground.xcworkspace/xcuserdata
|
||||
.swiftpm/xcode/package.xcworkspace/xcuserdata
|
||||
.swiftpm/xcode/xcuserdata
|
||||
Playground_iOS.playground/playground.xcworkspace/xcuserdata
|
||||
LegacyDemo/LegacyDemo.xcodeproj/xcuserdata
|
||||
Demo/Demo.xcodeproj/xcuserdata
|
||||
|
||||
15
.jazzy.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
author: John Estropia
|
||||
author_url: https://github.com/JohnEstropia
|
||||
github_url: https://github.com/JohnEstropia/CoreStore
|
||||
module: CoreStore
|
||||
readme: README.md
|
||||
include: Sources/*
|
||||
output: docs
|
||||
theme: fullwidth
|
||||
clean: true
|
||||
skip_undocumented: true
|
||||
xcodebuild_arguments:
|
||||
- -sdk
|
||||
- iphonesimulator
|
||||
- -scheme
|
||||
- CoreStore iOS
|
||||
@@ -1 +0,0 @@
|
||||
3.0.1
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:CoreStoreDemo.xcodeproj">
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -2,9 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
|
||||
<true/>
|
||||
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>CoreStore.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
44
.travis.yml
@@ -1,44 +0,0 @@
|
||||
language: objective-c
|
||||
osx_image: xcode8.2
|
||||
sudo: false
|
||||
git:
|
||||
submodules: false
|
||||
notifications:
|
||||
email: false
|
||||
env:
|
||||
global:
|
||||
- LC_CTYPE=en_US.UTF-8
|
||||
- LANG=en_US.UTF-8
|
||||
matrix:
|
||||
- DESTINATION="OS=10.1,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.3,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.3,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.12 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=3.1,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.1 RUN_TESTS="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.1 RUN_TESTS="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=10.1,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.1 RUN_TESTS="YES" POD_LINT="NO"
|
||||
- DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.1 RUN_TESTS="YES" POD_LINT="NO"
|
||||
before_install:
|
||||
- gem install cocoapods --no-rdoc --no-ri --no-document --quiet
|
||||
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
|
||||
- curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.18/Carthage.pkg"
|
||||
- sudo installer -pkg "Carthage.pkg" -target /
|
||||
- rm "Carthage.pkg"
|
||||
before_script:
|
||||
- carthage update --use-submodules
|
||||
script:
|
||||
- set -o pipefail
|
||||
- xcodebuild -version
|
||||
- xcodebuild -showsdks
|
||||
- if [ $RUN_TESTS == "YES" ]; then
|
||||
xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
||||
xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
||||
fi
|
||||
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.2" -destination "OS=10.1,name=iPhone 7" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
||||
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.2" -destination "OS=10.1,name=iPhone 7" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
||||
- if [ $POD_LINT == "YES" ]; then
|
||||
pod lib lint --quick;
|
||||
fi
|
||||
BIN
CoreStore.png
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 61 KiB |
@@ -1,22 +1,22 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "CoreStore"
|
||||
s.version = "3.0.2"
|
||||
s.version = "8.0.1"
|
||||
s.swift_version = "5.4"
|
||||
s.license = "MIT"
|
||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||
s.documentation_url = "https://JohnEstropia.github.io/CoreStore"
|
||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||
s.author = { "John Rommel Estropia" => "rommel.estropia@gmail.com" }
|
||||
s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = "8.0"
|
||||
s.osx.deployment_target = "10.10"
|
||||
s.watchos.deployment_target = "2.0"
|
||||
s.tvos.deployment_target = "9.0"
|
||||
s.ios.deployment_target = "11.0"
|
||||
s.osx.deployment_target = "10.13"
|
||||
s.watchos.deployment_target = "4.0"
|
||||
s.tvos.deployment_target = "11.0"
|
||||
|
||||
s.source_files = "Sources", "Sources/**/*.{swift,h,m}"
|
||||
s.public_header_files = "Sources/**/*.h"
|
||||
s.frameworks = "Foundation", "CoreData"
|
||||
s.requires_arc = true
|
||||
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D USE_FRAMEWORKS -D DEBUG',
|
||||
'OTHER_SWIFT_FLAGS[config=Release]' => '-D USE_FRAMEWORKS',
|
||||
'GCC_PREPROCESSOR_DEFINITIONS' => 'USE_FRAMEWORKS=1' }
|
||||
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D DEBUG', 'OTHER_LDFLAGS' => '-weak_framework Combine -weak_framework SwiftUI' }
|
||||
end
|
||||
|
||||
BIN
CoreStore.sketch
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore OSX"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -39,17 +48,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore OSX"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -70,8 +68,6 @@
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -41,6 +41,22 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore iOS"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "NSZombieEnabled"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -53,17 +69,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore iOS"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -84,8 +89,12 @@
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "-com.apple.CoreData.SQLDebug 2"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore tvOS"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -39,17 +48,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
|
||||
BuildableName = "CoreStore.framework"
|
||||
BlueprintName = "CoreStore tvOS"
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -70,8 +68,6 @@
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -29,8 +29,6 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -51,8 +49,6 @@
|
||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
5
CoreStore.xcworkspace/contents.xcworkspacedata
generated
@@ -1,10 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Demo/Demo.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:CoreStore.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:CoreStoreDemo/CoreStoreDemo.xcodeproj">
|
||||
location = "group:LegacyDemo/LegacyDemo.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>B6855E48-4B19-4321-B1C7-CB2706E12777</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>CoreStoreDemo</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>4B60F1BCB491FF717C56441AE7783C74F417BE48</key>
|
||||
<string>github.com:JohnEstropia/CoreStore.git</string>
|
||||
<key>8B2E522D57154DFA93A06982C36315ECBEA4FA97</key>
|
||||
<string>github.com:JohnEstropia/GCDKit.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>CoreStoreDemo/CoreStoreDemo.xcodeproj</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>4B60F1BCB491FF717C56441AE7783C74F417BE48</key>
|
||||
<string>../../..</string>
|
||||
<key>8B2E522D57154DFA93A06982C36315ECBEA4FA97</key>
|
||||
<string>../../..Libraries/GCDKit</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>github.com:JohnEstropia/CoreStore.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>4B60F1BCB491FF717C56441AE7783C74F417BE48</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>4B60F1BCB491FF717C56441AE7783C74F417BE48</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>CoreStore</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>8B2E522D57154DFA93A06982C36315ECBEA4FA97</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>GCDKit</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "4B60F1BCB491FF717C56441AE7783C74F417BE48",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
|
||||
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
|
||||
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : 0,
|
||||
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : 0
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "B6855E48-4B19-4321-B1C7-CB2706E12777",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
||||
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit",
|
||||
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore"
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStoreDemo",
|
||||
"DVTSourceControlWorkspaceBlueprintVersion" : 203,
|
||||
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStoreDemo\/CoreStoreDemo.xcodeproj",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:JohnEstropia\/CoreStore.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "4B60F1BCB491FF717C56441AE7783C74F417BE48"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:JohnEstropia\/GCDKit.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8B2E522D57154DFA93A06982C36315ECBEA4FA97"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>7C5E31AC-5DD0-43DA-A5C6-AF73B4532D86</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>project</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>4B60F1BCB491FF717C56441AE7783C74F417BE48</key>
|
||||
<string>github.com:JohnEstropia/HardcoreData.git</string>
|
||||
<key>8B2E522D57154DFA93A06982C36315ECBEA4FA97</key>
|
||||
<string>github.com:JohnEstropia/GCDKit.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>4B60F1BCB491FF717C56441AE7783C74F417BE48</key>
|
||||
<string>../../..</string>
|
||||
<key>8B2E522D57154DFA93A06982C36315ECBEA4FA97</key>
|
||||
<string>../../..Libraries/GCDKit</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>github.com:JohnEstropia/HardcoreData.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>4B60F1BCB491FF717C56441AE7783C74F417BE48</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>8B2E522D57154DFA93A06982C36315ECBEA4FA97</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>GCDKit</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>4B60F1BCB491FF717C56441AE7783C74F417BE48</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>HardcoreData</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<<<<<<< Updated upstream
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
=======
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
|
||||
>>>>>>> Stashed changes
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<<<<<<< Updated upstream
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2015 John Rommel Estropia. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
=======
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 John Rommel Estropia. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439.5" width="441" height="20.5"/>
|
||||
>>>>>>> Stashed changes
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.92549019607843142" green="0.94117647058823528" blue="0.94509803921568625" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CoreStore" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="133" width="441" height="57.5"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="50"/>
|
||||
<color key="textColor" red="0.92549019607843142" green="0.94117647058823528" blue="0.94509803921568625" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.20392156862745098" green="0.28627450980392155" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10169.1" systemVersion="15D21" minimumToolsVersion="Automatic">
|
||||
<entity name="Palette" representedClassName="CoreStoreDemo.Palette">
|
||||
<attribute name="brightness" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="colorName" optional="YES" transient="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="hue" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="saturation" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
||||
<userInfo/>
|
||||
</entity>
|
||||
<entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES">
|
||||
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="subtitle" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES">
|
||||
<attribute name="abbreviation" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="secondsFromGMT" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
|
||||
</entity>
|
||||
<configuration name="FetchingAndQueryingDemo">
|
||||
<memberEntity name="TimeZone"/>
|
||||
</configuration>
|
||||
<configuration name="ObservingDemo">
|
||||
<memberEntity name="Palette"/>
|
||||
</configuration>
|
||||
<configuration name="TransactionsDemo">
|
||||
<memberEntity name="Place"/>
|
||||
</configuration>
|
||||
<elements>
|
||||
<element name="Palette" positionX="261" positionY="189" width="128" height="105"/>
|
||||
<element name="Place" positionX="261" positionY="225" width="128" height="105"/>
|
||||
<element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -1,307 +0,0 @@
|
||||
//
|
||||
// ListObserverDemoViewController.swift
|
||||
// CoreStoreDemo
|
||||
//
|
||||
// Created by John Rommel Estropia on 2015/05/02.
|
||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreStore
|
||||
|
||||
|
||||
private struct Static {
|
||||
|
||||
enum Filter: String {
|
||||
|
||||
case all = "All Colors"
|
||||
case light = "Light Colors"
|
||||
case dark = "Dark Colors"
|
||||
|
||||
func next() -> Filter {
|
||||
|
||||
switch self {
|
||||
|
||||
case .all: return .light
|
||||
case .light: return .dark
|
||||
case .dark: return .all
|
||||
}
|
||||
}
|
||||
|
||||
func whereClause() -> Where {
|
||||
|
||||
switch self {
|
||||
|
||||
case .all: return Where(true)
|
||||
case .light: return Where("%K >= %@", #keyPath(Palette.brightness), 0.9)
|
||||
case .dark: return Where("%K <= %@", #keyPath(Palette.brightness), 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static var filter = Filter.all {
|
||||
|
||||
didSet {
|
||||
|
||||
self.palettes.refetch(self.filter.whereClause())
|
||||
}
|
||||
}
|
||||
|
||||
static let palettes: ListMonitor<Palette> = {
|
||||
|
||||
try! CoreStore.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "ColorsDemo.sqlite",
|
||||
configuration: "ObservingDemo",
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
|
||||
return CoreStore.monitorSectionedList(
|
||||
From<Palette>(),
|
||||
SectionBy(#keyPath(Palette.colorName)),
|
||||
OrderBy(.ascending(#keyPath(Palette.hue)))
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListObserverDemoViewController
|
||||
|
||||
class ListObserverDemoViewController: UITableViewController, ListSectionObserver {
|
||||
|
||||
// MARK: NSObject
|
||||
|
||||
deinit {
|
||||
|
||||
Static.palettes.removeObserver(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: UIViewController
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
let navigationItem = self.navigationItem
|
||||
navigationItem.leftBarButtonItems = [
|
||||
self.editButtonItem,
|
||||
UIBarButtonItem(
|
||||
barButtonSystemItem: .trash,
|
||||
target: self,
|
||||
action: #selector(self.resetBarButtonItemTouched(_:))
|
||||
)
|
||||
]
|
||||
|
||||
let filterBarButton = UIBarButtonItem(
|
||||
title: Static.filter.rawValue,
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(self.filterBarButtonItemTouched(_:))
|
||||
)
|
||||
navigationItem.rightBarButtonItems = [
|
||||
UIBarButtonItem(
|
||||
barButtonSystemItem: .add,
|
||||
target: self,
|
||||
action: #selector(self.addBarButtonItemTouched(_:))
|
||||
),
|
||||
filterBarButton
|
||||
]
|
||||
self.filterBarButton = filterBarButton
|
||||
|
||||
Static.palettes.addObserver(self)
|
||||
|
||||
self.setTable(enabled: !Static.palettes.isPendingRefetch)
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
|
||||
super.prepare(for: segue, sender: sender)
|
||||
|
||||
switch (segue.identifier, segue.destination, sender) {
|
||||
|
||||
case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as Palette):
|
||||
destinationViewController.palette = palette
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: UITableViewDataSource
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return Static.palettes.numberOfSections()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return Static.palettes.numberOfObjectsInSection(section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
||||
|
||||
let palette = Static.palettes[indexPath]
|
||||
cell.colorView?.backgroundColor = palette.color
|
||||
cell.label?.text = palette.colorText
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
// MARK: UITableViewDelegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
||||
self.performSegue(
|
||||
withIdentifier: "ObjectObserverDemoViewController",
|
||||
sender: Static.palettes[indexPath]
|
||||
)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
|
||||
|
||||
switch editingStyle {
|
||||
|
||||
case .delete:
|
||||
let palette = Static.palettes[indexPath]
|
||||
CoreStore.beginAsynchronous{ (transaction) -> Void in
|
||||
|
||||
transaction.delete(palette)
|
||||
transaction.commit { (result) -> Void in }
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return Static.palettes.sectionInfoAtIndex(section).name
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListObserver
|
||||
|
||||
func listMonitorWillChange(_ monitor: ListMonitor<Palette>) {
|
||||
|
||||
self.tableView.beginUpdates()
|
||||
}
|
||||
|
||||
func listMonitorDidChange(_ monitor: ListMonitor<Palette>) {
|
||||
|
||||
self.tableView.endUpdates()
|
||||
}
|
||||
|
||||
func listMonitorWillRefetch(_ monitor: ListMonitor<Palette>) {
|
||||
|
||||
self.setTable(enabled: false)
|
||||
}
|
||||
|
||||
func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) {
|
||||
|
||||
self.filterBarButton?.title = Static.filter.rawValue
|
||||
self.tableView.reloadData()
|
||||
self.setTable(enabled: true)
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListObjectObserver
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didInsertObject object: Palette, toIndexPath indexPath: IndexPath) {
|
||||
|
||||
self.tableView.insertRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didDeleteObject object: Palette, fromIndexPath indexPath: IndexPath) {
|
||||
|
||||
self.tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didUpdateObject object: Palette, atIndexPath indexPath: IndexPath) {
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell {
|
||||
|
||||
let palette = Static.palettes[indexPath]
|
||||
cell.colorView?.backgroundColor = palette.color
|
||||
cell.label?.text = palette.colorText
|
||||
}
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didMoveObject object: Palette, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
|
||||
|
||||
self.tableView.deleteRows(at: [fromIndexPath], with: .automatic)
|
||||
self.tableView.insertRows(at: [toIndexPath], with: .automatic)
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListSectionObserver
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
||||
|
||||
self.tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic)
|
||||
}
|
||||
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Palette>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
||||
|
||||
self.tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var filterBarButton: UIBarButtonItem?
|
||||
|
||||
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
CoreStore.beginAsynchronous { (transaction) -> Void in
|
||||
|
||||
transaction.deleteAll(From<Palette>())
|
||||
transaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
Static.filter = Static.filter.next()
|
||||
}
|
||||
|
||||
@IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
CoreStore.beginAsynchronous { (transaction) -> Void in
|
||||
|
||||
let palette = transaction.create(Into<Palette>())
|
||||
palette.setInitialValues()
|
||||
|
||||
transaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private func setTable(enabled: Bool) {
|
||||
|
||||
UIView.animate(
|
||||
withDuration: 0.2,
|
||||
delay: 0,
|
||||
options: .beginFromCurrentState,
|
||||
animations: { () -> Void in
|
||||
|
||||
if let tableView = self.tableView {
|
||||
|
||||
tableView.alpha = enabled ? 1.0 : 0.5
|
||||
tableView.isUserInteractionEnabled = enabled
|
||||
}
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
//
|
||||
// ObserversViewController.swift
|
||||
// CoreStoreDemo
|
||||
//
|
||||
// Created by John Rommel Estropia on 2015/05/24.
|
||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
// MARK: - ObserversViewController
|
||||
|
||||
class ObserversViewController: UIViewController {
|
||||
|
||||
// MARK: UIViewController
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
let alert = UIAlertController(
|
||||
title: "Observers Demo",
|
||||
message: "This demo shows how to observe changes to a list of objects. The top and bottom view controllers both observe a single shared \"ListMonitor\" instance.\n\nTap on a row to see how to observe changes made to a single object using a \"ObjectMonitor\".",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// BaseTestCase.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,12 +36,11 @@ class BaseTestCase: XCTestCase {
|
||||
// MARK: Internal
|
||||
|
||||
@nonobjc
|
||||
@discardableResult
|
||||
func prepareStack<T>(configurations: [String?] = [nil], _ closure: (_ dataStack: DataStack) -> T) -> T {
|
||||
func prepareStack(configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) throws -> Void) {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
do {
|
||||
|
||||
@@ -51,36 +50,66 @@ class BaseTestCase: XCTestCase {
|
||||
SQLiteStore(
|
||||
fileURL: SQLiteStore.defaultRootDirectory
|
||||
.appendingPathComponent(UUID().uuidString)
|
||||
.appendingPathComponent("\(type(of: self))_\(($0 ?? "-null-")).sqlite"),
|
||||
.appendingPathComponent("\(Self.self)_\(($0 ?? "-null-")).sqlite"),
|
||||
configuration: $0,
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
}
|
||||
try closure(stack)
|
||||
}
|
||||
catch let error as NSError {
|
||||
|
||||
XCTFail(error.coreStoreDumpString)
|
||||
}
|
||||
return closure(stack)
|
||||
self.addTeardownBlock {
|
||||
stack.unsafeRemoveAllPersistentStoresAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
func expectLogger<T>(_ expectations: [TestLogger.Expectation], closure: () -> T) -> T {
|
||||
func expectLogger<T>(_ expectations: [TestLogger.Expectation], closure: () throws -> T) rethrows -> T {
|
||||
|
||||
CoreStore.logger = TestLogger(self.prepareLoggerExpectations(expectations))
|
||||
CoreStoreDefaults.logger = TestLogger(self.prepareLoggerExpectations(expectations))
|
||||
defer {
|
||||
|
||||
self.checkExpectationsImmediately()
|
||||
CoreStore.logger = TestLogger([:])
|
||||
CoreStoreDefaults.logger = TestLogger([:])
|
||||
}
|
||||
return closure()
|
||||
return try closure()
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
func expectLogger(_ expectations: [TestLogger.Expectation: XCTestExpectation]) {
|
||||
|
||||
CoreStore.logger = TestLogger(expectations)
|
||||
CoreStoreDefaults.logger = TestLogger(expectations)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
func expectError<T>(code: CoreStoreErrorCode, closure: () throws -> T) {
|
||||
|
||||
CoreStoreDefaults.logger = TestLogger(self.prepareLoggerExpectations([.logError]))
|
||||
defer {
|
||||
|
||||
self.checkExpectationsImmediately()
|
||||
CoreStoreDefaults.logger = TestLogger([:])
|
||||
}
|
||||
do {
|
||||
|
||||
_ = try closure()
|
||||
}
|
||||
catch let error as CoreStoreError {
|
||||
|
||||
if error.errorCode == code.rawValue {
|
||||
|
||||
return
|
||||
}
|
||||
XCTFail("Expected error code \(code) different from actual error: \((error as NSError).coreStoreDumpString)")
|
||||
}
|
||||
catch {
|
||||
|
||||
XCTFail("Error not wrapped as \(Internals.typeName(CoreStoreError.self)): \((error as NSError).coreStoreDumpString)")
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@@ -112,12 +141,12 @@ class BaseTestCase: XCTestCase {
|
||||
|
||||
super.setUp()
|
||||
self.deleteStores()
|
||||
CoreStore.logger = TestLogger([:])
|
||||
CoreStoreDefaults.logger = TestLogger([:])
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
CoreStore.logger = DefaultLogger()
|
||||
CoreStoreDefaults.logger = DefaultLogger()
|
||||
self.deleteStores()
|
||||
super.tearDown()
|
||||
}
|
||||
94
CoreStoreTests/BaseTestDataTestCase.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// BaseTestDataTestCase.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - BaseTestDataTestCase
|
||||
|
||||
class BaseTestDataTestCase: BaseTestCase {
|
||||
|
||||
@nonobjc
|
||||
let dateFormatter: DateFormatter = Internals.with {
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
formatter.timeZone = TimeZone(identifier: "UTC")
|
||||
formatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian)
|
||||
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
|
||||
return formatter
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
func prepareTestDataForStack(_ stack: DataStack, configurations: [ModelConfiguration] = [nil]) {
|
||||
|
||||
try! stack.perform(
|
||||
synchronous: { (transaction) in
|
||||
|
||||
for (configurationIndex, configuration) in configurations.enumerated() {
|
||||
|
||||
let configurationOrdinal = configurationIndex + 1
|
||||
if configuration == nil || configuration == "Config1" {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 100) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity1:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
if configuration == nil || configuration == "Config2" {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity2>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 200) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity2:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
//
|
||||
// BaseTestDataTestCase.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Created by John Rommel Estropia on 2016/06/11.
|
||||
// Copyright © 2016 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - BaseTestDataTestCase
|
||||
|
||||
class BaseTestDataTestCase: BaseTestCase {
|
||||
|
||||
@nonobjc
|
||||
let dateFormatter: DateFormatter = {
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
formatter.timeZone = TimeZone(identifier: "UTC")
|
||||
formatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian)
|
||||
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
@nonobjc
|
||||
func prepareTestDataForStack(_ stack: DataStack, configurations: [String?] = [nil]) {
|
||||
|
||||
stack.beginSynchronous { (transaction) in
|
||||
|
||||
for (configurationIndex, configuration) in configurations.enumerated() {
|
||||
|
||||
let configurationOrdinal = configurationIndex + 1
|
||||
if configuration == nil || configuration == "Config1" {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 100) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity1:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
if configuration == nil || configuration == "Config2" {
|
||||
|
||||
for idIndex in 1 ... 5 {
|
||||
|
||||
let object = transaction.create(Into<TestEntity2>(configuration))
|
||||
object.testEntityID = NSNumber(value: (configurationOrdinal * 200) + idIndex)
|
||||
|
||||
object.testNumber = NSNumber(value: idIndex)
|
||||
object.testDate = self.dateFormatter.date(from: "2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
||||
object.testBoolean = NSNumber(value: (idIndex % 2) == 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
||||
|
||||
let string = "\(configuration ?? "nil"):TestEntity2:\(idIndex)"
|
||||
object.testString = string
|
||||
object.testData = (string as NSString).data(using: String.Encoding.utf8.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = transaction.commitAndWait()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// BridgingTests.h
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// BridgingTests.m
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,6 +30,10 @@
|
||||
|
||||
@import CoreData;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
|
||||
// MARK: - BridgingTests
|
||||
|
||||
@implementation BridgingTests
|
||||
@@ -163,19 +167,13 @@
|
||||
- (void)test_ThatDataStacks_BridgeCorrectly {
|
||||
|
||||
CSDataStack *dataStack = [[CSDataStack alloc]
|
||||
initWithModelName:@"Model"
|
||||
initWithXcodeModelName:@"Model"
|
||||
bundle:[NSBundle bundleForClass:[self class]]
|
||||
versionChain:nil];
|
||||
XCTAssertNotNil(dataStack);
|
||||
|
||||
[CSCoreStore setDefaultStack:dataStack];
|
||||
XCTAssertTrue([dataStack isEqual:[CSCoreStore defaultStack]]);
|
||||
}
|
||||
|
||||
- (void)test_ThatStorages_BridgeCorrectly {
|
||||
|
||||
NSError *memoryError;
|
||||
CSInMemoryStore *memoryStorage = [CSCoreStore
|
||||
CSInMemoryStore *memoryStorage = [dataStack
|
||||
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
||||
error:&memoryError];
|
||||
XCTAssertNotNil(memoryStorage);
|
||||
@@ -186,84 +184,78 @@
|
||||
XCTAssertNil(memoryError);
|
||||
|
||||
NSError *sqliteError;
|
||||
CSSQLiteStore *sqliteStorage = [CSCoreStore
|
||||
CSSQLiteStore *sqliteStorage = [dataStack
|
||||
addSQLiteStorageAndWait:[CSSQLiteStore new]
|
||||
error:&sqliteError];
|
||||
XCTAssertNotNil(sqliteStorage);
|
||||
XCTAssertEqualObjects([[sqliteStorage class] storeType], [CSSQLiteStore storeType]);
|
||||
XCTAssertEqualObjects([[sqliteStorage class] storeType], NSSQLiteStoreType);
|
||||
XCTAssertNil(sqliteStorage.configuration);
|
||||
XCTAssertEqualObjects(sqliteStorage.storeOptions, @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" } });
|
||||
NSDictionary *storeOptions = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" },
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: @YES };
|
||||
XCTAssertEqualObjects(sqliteStorage.storeOptions, storeOptions);
|
||||
XCTAssertNil(sqliteError);
|
||||
}
|
||||
|
||||
- (void)test_ThatTransactions_BridgeCorrectly {
|
||||
|
||||
[CSCoreStore
|
||||
setDefaultStack:[[CSDataStack alloc]
|
||||
initWithModelName:@"Model"
|
||||
bundle:[NSBundle bundleForClass:[self class]]
|
||||
versionChain:nil]];
|
||||
[CSCoreStore
|
||||
|
||||
CSDataStack *dataStack = [[CSDataStack alloc]
|
||||
initWithXcodeModelName:@"Model"
|
||||
bundle:[NSBundle bundleForClass:[self class]]
|
||||
versionChain:nil];
|
||||
XCTAssertNotNil(dataStack);
|
||||
|
||||
[dataStack
|
||||
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
||||
error:nil];
|
||||
|
||||
{
|
||||
CSUnsafeDataTransaction *transaction = [CSCoreStore beginUnsafe];
|
||||
CSUnsafeDataTransaction *transaction = [dataStack beginUnsafe];
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSUnsafeDataTransaction class]]);
|
||||
NSError *error;
|
||||
BOOL result = [transaction commitAndWaitWithError:&error];
|
||||
XCTAssertTrue(result);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
{
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"sync"];
|
||||
[CSCoreStore beginSynchronous:^(CSSynchronousDataTransaction * _Nonnull transaction) {
|
||||
|
||||
NSError *error;
|
||||
BOOL result =
|
||||
[dataStack
|
||||
beginSynchronous:^(CSSynchronousDataTransaction * _Nonnull transaction) {
|
||||
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSSynchronousDataTransaction class]]);
|
||||
NSError *error;
|
||||
XCTAssertTrue([transaction commitAndWaitWithError:&error]);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}
|
||||
error:&error];
|
||||
XCTAssertTrue(result);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
{
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"async"];
|
||||
[CSCoreStore beginAsynchronous:^(CSAsynchronousDataTransaction * _Nonnull transaction) {
|
||||
[dataStack beginAsynchronous:^(CSAsynchronousDataTransaction * _Nonnull transaction) {
|
||||
|
||||
XCTAssertNotNil(transaction);
|
||||
XCTAssert([transaction isKindOfClass:[CSAsynchronousDataTransaction class]]);
|
||||
[expectation fulfill];
|
||||
[transaction
|
||||
commitWithSuccess:^{
|
||||
|
||||
[expectation fulfill];
|
||||
}
|
||||
failure:^(CSError *error){
|
||||
|
||||
XCTFail();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_WATCHOS || TARGET_OS_TV
|
||||
|
||||
- (void)test_ThatDataStacks_CanCreateCustomFetchedResultsControllers {
|
||||
|
||||
[CSCoreStore
|
||||
setDefaultStack:[[CSDataStack alloc]
|
||||
initWithModelName:@"Model"
|
||||
bundle:[NSBundle bundleForClass:[self class]]
|
||||
versionChain:nil]];
|
||||
[CSCoreStore
|
||||
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
||||
error:nil];
|
||||
NSFetchedResultsController *controller =
|
||||
[[CSCoreStore defaultStack]
|
||||
createFetchedResultsControllerFrom:CSFromClass([TestEntity1 class])
|
||||
sectionBy:[CSSectionBy keyPath:CSKeyPath(TestEntity1, testString)]
|
||||
fetchClauses:@[CSWhereFormat(@"%K > %d", CSKeyPath(TestEntity1, testEntityID), 100),
|
||||
CSOrderByKeys(CSSortAscending(CSKeyPath(TestEntity1, testString)), nil),
|
||||
CSTweakRequest(^(NSFetchRequest *fetchRequest) { fetchRequest.fetchLimit = 10; })]];
|
||||
|
||||
XCTAssertNotNil(controller);
|
||||
XCTAssertEqualObjects(controller.fetchRequest.entity.managedObjectClassName, [[TestEntity1 class] description]);
|
||||
XCTAssertEqualObjects(controller.sectionNameKeyPath, CSKeyPath(TestEntity1, testString));
|
||||
XCTAssertEqualObjects(controller.fetchRequest.predicate,
|
||||
CSWhereFormat(@"%K > %d", CSKeyPath(TestEntity1, testEntityID), 100).predicate);
|
||||
XCTAssertEqualObjects(controller.fetchRequest.sortDescriptors,
|
||||
CSOrderByKeys(CSSortAscending(CSKeyPath(TestEntity1, testString)), nil).sortDescriptors);
|
||||
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// ConvenienceTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,12 +23,13 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
||||
|
||||
// MARK: - ConvenienceTests
|
||||
|
||||
class ConvenienceTests: BaseTestCase {
|
||||
@@ -41,8 +42,8 @@ class ConvenienceTests: BaseTestCase {
|
||||
let controller = stack.createFetchedResultsController(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testString)),
|
||||
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testString))),
|
||||
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))),
|
||||
Tweak { $0.fetchLimit = 10 }
|
||||
)
|
||||
XCTAssertEqual(controller.managedObjectContext, stack.mainContext)
|
||||
@@ -50,11 +51,11 @@ class ConvenienceTests: BaseTestCase {
|
||||
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
|
||||
XCTAssertEqual(
|
||||
controller.fetchRequest.sortDescriptors!,
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||
)
|
||||
XCTAssertEqual(
|
||||
controller.fetchRequest.predicate,
|
||||
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||
)
|
||||
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
|
||||
}
|
||||
@@ -65,13 +66,13 @@ class ConvenienceTests: BaseTestCase {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
_ = withExtendedLifetime(stack.beginUnsafe()) { (transaction: UnsafeDataTransaction) in
|
||||
withExtendedLifetime(stack.beginUnsafe()) { (transaction: UnsafeDataTransaction) in
|
||||
|
||||
let controller = transaction.createFetchedResultsController(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testString)),
|
||||
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testString))),
|
||||
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))),
|
||||
Tweak { $0.fetchLimit = 10 }
|
||||
)
|
||||
XCTAssertEqual(controller.managedObjectContext, transaction.context)
|
||||
@@ -79,16 +80,14 @@ class ConvenienceTests: BaseTestCase {
|
||||
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
|
||||
XCTAssertEqual(
|
||||
controller.fetchRequest.sortDescriptors!,
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||
)
|
||||
XCTAssertEqual(
|
||||
controller.fetchRequest.predicate,
|
||||
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||
)
|
||||
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
535
CoreStoreTests/DynamicModelTests.swift
Normal file
@@ -0,0 +1,535 @@
|
||||
//
|
||||
// DynamicModelTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
#if os(macOS)
|
||||
typealias Color = NSColor
|
||||
|
||||
#else
|
||||
typealias Color = UIColor
|
||||
|
||||
#endif
|
||||
|
||||
class Animal: CoreStoreObject {
|
||||
|
||||
@Field.Stored("species")
|
||||
var species: String = "Swift"
|
||||
|
||||
@Field.Coded("color", coder: FieldCoders.NSCoding.self)
|
||||
var color: Color? = .blue
|
||||
|
||||
@Field.Relationship("master")
|
||||
var master: Person?
|
||||
}
|
||||
|
||||
class Dog: Animal {
|
||||
|
||||
static let commonNicknames = ["Spot", "Benjie", "Max", "Milo"]
|
||||
|
||||
@Field.Stored(
|
||||
"nickname",
|
||||
dynamicInitialValue: {
|
||||
commonNicknames.randomElement()!
|
||||
}
|
||||
)
|
||||
var nickname: String
|
||||
|
||||
@Field.Stored("age")
|
||||
var age: Int = 1
|
||||
|
||||
@Field.Relationship("friends")
|
||||
var friends: [Dog]
|
||||
|
||||
@Field.Relationship("friendedBy", inverse: \.$friends)
|
||||
var friendedBy: Set<Dog>
|
||||
}
|
||||
|
||||
struct CustomType {
|
||||
var string = "customString"
|
||||
}
|
||||
|
||||
enum Job: String, CaseIterable {
|
||||
|
||||
case unemployed
|
||||
case engineer
|
||||
case doctor
|
||||
case lawyer
|
||||
|
||||
init?(data: Data) {
|
||||
|
||||
guard
|
||||
let rawValue = String(data: data, encoding: .utf8),
|
||||
let value = Self.init(rawValue: rawValue)
|
||||
else {
|
||||
|
||||
return nil
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
func toData() -> Data {
|
||||
|
||||
return Data(self.rawValue.utf8)
|
||||
}
|
||||
}
|
||||
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Stored(
|
||||
"title",
|
||||
customSetter: { (object, field, newValue) in
|
||||
field.primitiveValue = newValue
|
||||
object.$displayName.primitiveValue = nil
|
||||
}
|
||||
)
|
||||
var title: String = "Mr."
|
||||
|
||||
@Field.Stored(
|
||||
"name",
|
||||
customSetter: { (object, field, newValue) in
|
||||
field.primitiveValue = newValue
|
||||
object.$displayName.primitiveValue = nil
|
||||
}
|
||||
)
|
||||
var name: String = ""
|
||||
|
||||
@Field.Virtual(
|
||||
"displayName",
|
||||
customGetter: Person.getDisplayName(_:_:),
|
||||
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
||||
)
|
||||
var displayName: String?
|
||||
|
||||
@Field.Virtual(
|
||||
"customType",
|
||||
customGetter: { (object, field) in
|
||||
|
||||
if let value = field.primitiveValue {
|
||||
|
||||
return value
|
||||
}
|
||||
let value = CustomType()
|
||||
field.primitiveValue = value
|
||||
return value
|
||||
}
|
||||
)
|
||||
var customField: CustomType
|
||||
|
||||
@Field.Coded(
|
||||
"job",
|
||||
coder: (
|
||||
encode: { $0.toData() },
|
||||
decode: { $0.flatMap(Job.init(data:)) ?? .unemployed }
|
||||
),
|
||||
dynamicInitialValue: {
|
||||
Job.allCases.randomElement()!
|
||||
}
|
||||
)
|
||||
var job: Job
|
||||
|
||||
@Field.Relationship("spouse")
|
||||
var spouse: Person?
|
||||
|
||||
@Field.Relationship("pets", inverse: \.$master)
|
||||
var pets: Set<Animal>
|
||||
|
||||
@Field.Relationship("_spouseInverse", inverse: \.$spouse)
|
||||
private var spouseInverse: Person?
|
||||
|
||||
private static func getDisplayName(_ object: ObjectProxy<Person>, _ field: ObjectProxy<Person>.FieldProxy<String?>) -> String? {
|
||||
|
||||
if let value = field.primitiveValue {
|
||||
|
||||
return value
|
||||
}
|
||||
let title = object.$title.value
|
||||
let name = object.$name.value
|
||||
let value = "\(title) \(name)"
|
||||
field.primitiveValue = value
|
||||
return value
|
||||
}
|
||||
|
||||
private static func keyPathsAffectingDisplayName() -> Set<String> {
|
||||
|
||||
return [
|
||||
String(keyPath: \Person.$title),
|
||||
String(keyPath: \Person.$name)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DynamicModelTests
|
||||
|
||||
class DynamicModelTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatDynamicModels_CanBeDeclaredCorrectly() {
|
||||
|
||||
let dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
Entity<Animal>("Animal"),
|
||||
Entity<Dog>("Dog"),
|
||||
Entity<Person>("Person")
|
||||
],
|
||||
versionLock: [
|
||||
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
|
||||
"Dog": [0xad6de93adc5565d, 0x7897e51253eba5a3, 0xd12b9ce0b13600f3, 0x5a4827cd794cd15e],
|
||||
"Person": [0xf3e6ba6016bbedc6, 0x50dedf64f0eba490, 0xa32088a0ee83468d, 0xb72d1d0b37bd0992]
|
||||
]
|
||||
)
|
||||
)
|
||||
self.prepareStack(dataStack, configurations: [nil]) { (stack) in
|
||||
|
||||
let k1 = String(keyPath: \Animal.$species)
|
||||
XCTAssertEqual(k1, "species")
|
||||
|
||||
let k2 = String(keyPath: \Dog.$species)
|
||||
XCTAssertEqual(k2, "species")
|
||||
|
||||
let k3 = String(keyPath: \Dog.$nickname)
|
||||
XCTAssertEqual(k3, "nickname")
|
||||
|
||||
let updateDone = self.expectation(description: "update-done")
|
||||
let fetchDone = self.expectation(description: "fetch-done")
|
||||
let willSetPriorObserverDone = self.expectation(description: "willSet-observe-prior-done")
|
||||
let willSetNotPriorObserverDone = self.expectation(description: "willSet-observe-notPrior-done")
|
||||
let didSetObserverDone = self.expectation(description: "didSet-observe-done")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let animal = transaction.create(Into<Animal>())
|
||||
XCTAssertEqual(animal.species, "Swift")
|
||||
XCTAssertTrue(type(of: animal.species) == String.self)
|
||||
XCTAssertEqual(animal.color, Color.blue)
|
||||
|
||||
animal.species = "Sparrow"
|
||||
XCTAssertEqual(animal.species, "Sparrow")
|
||||
|
||||
animal.color = .yellow
|
||||
XCTAssertEqual(animal.color, Color.yellow)
|
||||
|
||||
for property in Animal.metaProperties(includeSuperclasses: true) {
|
||||
|
||||
switch property.keyPath {
|
||||
|
||||
case String(keyPath: \Animal.$species):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Stored<String>)
|
||||
|
||||
case String(keyPath: \Animal.$master):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Relationship<Person?>)
|
||||
|
||||
case String(keyPath: \Animal.$color):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Coded<Color?>)
|
||||
|
||||
default:
|
||||
XCTFail("Unknown KeyPath: \"\(property.keyPath)\"")
|
||||
}
|
||||
}
|
||||
|
||||
let dog = transaction.create(Into<Dog>())
|
||||
XCTAssertEqual(dog.species, "Swift")
|
||||
XCTAssertEqual(dog.age, 1)
|
||||
XCTAssertTrue(Dog.commonNicknames.contains(dog.nickname))
|
||||
|
||||
for property in Dog.metaProperties(includeSuperclasses: true) {
|
||||
|
||||
switch property.keyPath {
|
||||
|
||||
case String(keyPath: \Dog.$species):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Stored<String>)
|
||||
|
||||
case String(keyPath: \Dog.$master):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Relationship<Person?>)
|
||||
|
||||
case String(keyPath: \Dog.$color):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Coded<Color?>)
|
||||
|
||||
case String(keyPath: \Dog.$nickname):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Stored<String>)
|
||||
|
||||
case String(keyPath: \Dog.$age):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Stored<Int>)
|
||||
|
||||
case String(keyPath: \Dog.$friends):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Relationship<[Dog]>)
|
||||
|
||||
case String(keyPath: \Dog.$friendedBy):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Relationship<Set<Dog>>)
|
||||
|
||||
default:
|
||||
XCTFail("Unknown KeyPath: \"\(property.keyPath)\"")
|
||||
}
|
||||
}
|
||||
|
||||
let didSetObserver = dog.observe(\.$species, options: [.new, .old]) { (object, change) in
|
||||
|
||||
XCTAssertEqual(object, dog)
|
||||
XCTAssertEqual(change.kind, .setting)
|
||||
XCTAssertEqual(change.newValue, "Dog")
|
||||
XCTAssertEqual(change.oldValue, "Swift")
|
||||
XCTAssertFalse(change.isPrior)
|
||||
XCTAssertEqual(object.species, "Dog")
|
||||
didSetObserverDone.fulfill()
|
||||
}
|
||||
let willSetObserver = dog.observe(\.$species, options: [.new, .old, .prior]) { (object, change) in
|
||||
|
||||
XCTAssertEqual(object, dog)
|
||||
XCTAssertEqual(change.kind, .setting)
|
||||
XCTAssertEqual(change.oldValue, "Swift")
|
||||
|
||||
if change.isPrior {
|
||||
|
||||
XCTAssertNil(change.newValue)
|
||||
XCTAssertEqual(object.species, "Swift")
|
||||
willSetPriorObserverDone.fulfill()
|
||||
}
|
||||
else {
|
||||
|
||||
XCTAssertEqual(change.newValue, "Dog")
|
||||
XCTAssertEqual(object.species, "Dog")
|
||||
willSetNotPriorObserverDone.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
dog.species = "Dog"
|
||||
XCTAssertEqual(dog.species, "Dog")
|
||||
|
||||
didSetObserver.invalidate()
|
||||
willSetObserver.invalidate()
|
||||
|
||||
dog.nickname = "Spot"
|
||||
XCTAssertEqual(dog.nickname, "Spot")
|
||||
|
||||
let person = transaction.create(Into<Person>())
|
||||
XCTAssertTrue(person.pets.isEmpty)
|
||||
XCTAssertEqual(person.customField.string, "customString")
|
||||
let initialJob = person.job
|
||||
XCTAssertTrue(Job.allCases.contains(initialJob))
|
||||
|
||||
XCTAssertEqual(
|
||||
person.rawObject!
|
||||
.runtimeType()
|
||||
.keyPathsForValuesAffectingValue(forKey: "displayName"),
|
||||
["title", "name"]
|
||||
)
|
||||
|
||||
person.name = "Joe"
|
||||
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "name") as! String?, "Joe")
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "Mr. Joe")
|
||||
|
||||
person.rawObject!.setValue("AAAA", forKey: "displayName")
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "AAAA")
|
||||
|
||||
person.name = "John"
|
||||
XCTAssertEqual(person.name, "John")
|
||||
XCTAssertEqual(person.displayName, "Mr. John") // Custom getter
|
||||
|
||||
let personSnapshot1 = person.asSnapshot(in: transaction)!
|
||||
XCTAssertEqual(person.name, personSnapshot1.$name)
|
||||
XCTAssertEqual(person.title, personSnapshot1.$title)
|
||||
XCTAssertEqual(person.displayName, personSnapshot1.$displayName)
|
||||
XCTAssertEqual(person.job, personSnapshot1.$job)
|
||||
|
||||
person.title = "Sir"
|
||||
XCTAssertEqual(person.displayName, "Sir John")
|
||||
|
||||
XCTAssertEqual(personSnapshot1.$name, "John")
|
||||
XCTAssertEqual(personSnapshot1.$title, "Mr.")
|
||||
XCTAssertEqual(personSnapshot1.$displayName, "Mr. John")
|
||||
|
||||
person.customField.string = "newCustomString"
|
||||
XCTAssertEqual(person.customField.string, "newCustomString")
|
||||
|
||||
person.job = .engineer
|
||||
XCTAssertEqual(person.job, .engineer)
|
||||
|
||||
let personSnapshot2 = person.asSnapshot(in: transaction)!
|
||||
XCTAssertEqual(person.name, personSnapshot2.$name)
|
||||
XCTAssertEqual(person.title, personSnapshot2.$title)
|
||||
XCTAssertEqual(person.displayName, personSnapshot2.$displayName)
|
||||
XCTAssertEqual(person.job, personSnapshot2.$job)
|
||||
|
||||
var personSnapshot3 = personSnapshot2
|
||||
personSnapshot3.$name = "James"
|
||||
XCTAssertEqual(personSnapshot1.$name, "John")
|
||||
XCTAssertEqual(personSnapshot1.$displayName, "Mr. John")
|
||||
XCTAssertEqual(personSnapshot1.$job, initialJob)
|
||||
XCTAssertEqual(personSnapshot2.$name, "John")
|
||||
XCTAssertEqual(personSnapshot2.$displayName, "Sir John")
|
||||
XCTAssertEqual(personSnapshot2.$job, .engineer)
|
||||
XCTAssertEqual(personSnapshot3.$name, "James")
|
||||
XCTAssertEqual(personSnapshot3.$displayName, "Sir John")
|
||||
XCTAssertEqual(personSnapshot3.$job, .engineer)
|
||||
|
||||
|
||||
|
||||
person.pets.insert(dog)
|
||||
XCTAssertEqual(person.pets.count, 1)
|
||||
XCTAssertEqual(person.pets.first, dog)
|
||||
XCTAssertEqual(person.pets.first?.master, person)
|
||||
XCTAssertEqual(dog.master, person)
|
||||
XCTAssertEqual(dog.master?.pets.first, dog)
|
||||
},
|
||||
success: { _ in
|
||||
|
||||
let person = try! stack.fetchOne(From<Person>())
|
||||
XCTAssertNotNil(person)
|
||||
|
||||
let personPublisher = person!.asPublisher(in: stack)
|
||||
XCTAssertEqual(personPublisher.$name, "John")
|
||||
XCTAssertEqual(personPublisher.$displayName, "Sir John")
|
||||
XCTAssertEqual(personPublisher.$job, .engineer)
|
||||
|
||||
updateDone.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let p1 = Where<Animal>({ $0.$species == "Sparrow" })
|
||||
XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow"))
|
||||
|
||||
let bird = try transaction.fetchOne(From<Animal>(), p1)
|
||||
XCTAssertNotNil(bird)
|
||||
XCTAssertEqual(bird!.species, "Sparrow")
|
||||
XCTAssertEqual(bird!.color, Color.yellow)
|
||||
|
||||
let p2 = Where<Dog>({ $0.$nickname == "Spot" })
|
||||
XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot"))
|
||||
|
||||
let dog = try transaction.fetchOne(From<Dog>().where(\.$nickname == "Spot"))
|
||||
XCTAssertNotNil(dog)
|
||||
XCTAssertEqual(dog!.nickname, "Spot")
|
||||
XCTAssertEqual(dog!.species, "Dog")
|
||||
|
||||
let person = try transaction.fetchOne(From<Person>())
|
||||
XCTAssertNotNil(person)
|
||||
XCTAssertEqual(person!.name, "John")
|
||||
XCTAssertEqual(person!.title, "Sir")
|
||||
XCTAssertEqual(person!.displayName, "Sir John")
|
||||
XCTAssertEqual(person!.customField.string, "customString")
|
||||
XCTAssertEqual(person!.job, .engineer)
|
||||
XCTAssertEqual(person!.pets.first, dog)
|
||||
|
||||
let p3 = Where<Dog>({ $0.$age == 10 })
|
||||
XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10))
|
||||
|
||||
let totalAge = try transaction.queryValue(
|
||||
From<Dog>().select(Int.self, .sum(\.$age))
|
||||
)
|
||||
XCTAssertEqual(totalAge, 1)
|
||||
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>()
|
||||
.where(\Animal.$species == "Dog" && \Dog.$age == 10)
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>()
|
||||
.where(\Dog.$age == 10 && \Animal.$species == "Dog")
|
||||
.orderBy(.ascending({ $0.$species }))
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.$species == "Dog" && $0.$age == 10 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.$age == 10 && $0.$species == "Dog" })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
(\Dog.$age > 10 && \Dog.$age <= 15)
|
||||
)
|
||||
},
|
||||
success: { _ in
|
||||
|
||||
fetchDone.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
self.waitForExpectations(timeout: 10, handler: { _ in })
|
||||
|
||||
self.addTeardownBlock {
|
||||
dataStack.unsafeRemoveAllPersistentStoresAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
|
||||
|
||||
XCTAssertEqual(String(keyPath: \Animal.$species), "species")
|
||||
XCTAssertEqual(String(keyPath: \Dog.$species), "species")
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
func prepareStack(_ dataStack: DataStack, configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) -> Void) {
|
||||
|
||||
do {
|
||||
|
||||
try configurations.forEach { (configuration) in
|
||||
|
||||
try dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileURL: SQLiteStore.defaultRootDirectory
|
||||
.appendingPathComponent(UUID().uuidString)
|
||||
.appendingPathComponent("\(Self.self)_\((configuration ?? "-null-")).sqlite"),
|
||||
configuration: configuration,
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
catch let error as NSError {
|
||||
|
||||
XCTFail(error.coreStoreDumpString)
|
||||
}
|
||||
closure(dataStack)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// ErrorTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -31,6 +31,7 @@ import CoreStore
|
||||
|
||||
// MARK: - ErrorTests
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
final class ErrorTests: XCTestCase {
|
||||
|
||||
@objc
|
||||
@@ -85,16 +86,21 @@ final class ErrorTests: XCTestCase {
|
||||
|
||||
let dummyURL = URL(string: "file:///test1/test2.sqlite")!
|
||||
|
||||
let model = NSManagedObjectModel.fromBundle(Bundle(for: type(of: self)), modelName: "Model")
|
||||
let schemaHistory = SchemaHistory(
|
||||
XcodeDataModelSchema.from(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
)
|
||||
let version = "1.0.0"
|
||||
|
||||
let error = CoreStoreError.mappingModelNotFound(localStoreURL: dummyURL, targetModel: model, targetModelVersion: version)
|
||||
let error = CoreStoreError.mappingModelNotFound(localStoreURL: dummyURL, targetModel: schemaHistory.rawModel, targetModelVersion: version)
|
||||
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.mappingModelNotFound.rawValue)
|
||||
|
||||
let userInfo: NSDictionary = [
|
||||
"localStoreURL": dummyURL,
|
||||
"targetModel": model,
|
||||
"targetModel": schemaHistory.rawModel,
|
||||
"targetModelVersion": version
|
||||
]
|
||||
let objcError = error.bridgeToObjectiveC
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// FromTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -38,7 +39,7 @@ final class FromTests: BaseTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let from = From()
|
||||
let from = From<NSManagedObject>()
|
||||
XCTAssert(from.entityClass === NSManagedObject.self)
|
||||
XCTAssertNil(from.configurations)
|
||||
}
|
||||
@@ -74,33 +75,31 @@ final class FromTests: BaseTestCase {
|
||||
|
||||
let from = From<TestEntity1>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
}
|
||||
@@ -115,102 +114,98 @@ final class FromTests: BaseTestCase {
|
||||
|
||||
let from = From<TestEntity1>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||
}
|
||||
do {
|
||||
|
||||
|
||||
let from = From<TestEntity1>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
}
|
||||
@@ -225,99 +220,96 @@ final class FromTests: BaseTestCase {
|
||||
|
||||
let from = From<TestEntity1>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(Set(affectedConfigurations), ["PF_DEFAULT_CONFIGURATION_NAME", "Config1"] as Set)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
}
|
||||
@@ -332,96 +324,94 @@ final class FromTests: BaseTestCase {
|
||||
|
||||
let from = From<TestEntity1>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity1>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>()
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config1")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = self.expectLogger([.logWarning]) {
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
self.expectError(code: .persistentStoreNotFound) {
|
||||
|
||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
}
|
||||
XCTAssertFalse(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let from = From<TestEntity2>("Config2")
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertTrue(storesFound)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
XCTAssertNotNil(storesFound)
|
||||
XCTAssertNotNil(request.entity)
|
||||
XCTAssertNotNil(request.safeAffectedStores)
|
||||
XCTAssertNotNil(request.safeAffectedStores())
|
||||
|
||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||
|
||||
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
|
||||
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// GroupByTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -38,14 +39,14 @@ final class GroupByTests: BaseTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let groupBy = GroupBy()
|
||||
let groupBy = GroupBy<NSManagedObject>()
|
||||
XCTAssertEqual(groupBy, GroupBy([] as [String]))
|
||||
XCTAssertNotEqual(groupBy, GroupBy("key"))
|
||||
XCTAssertTrue(groupBy.keyPaths.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let groupBy = GroupBy("key1")
|
||||
let groupBy = GroupBy<NSManagedObject>("key1")
|
||||
XCTAssertEqual(groupBy, GroupBy("key1"))
|
||||
XCTAssertEqual(groupBy, GroupBy(["key1"]))
|
||||
XCTAssertNotEqual(groupBy, GroupBy("key2"))
|
||||
@@ -53,7 +54,7 @@ final class GroupByTests: BaseTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let groupBy = GroupBy("key1", "key2")
|
||||
let groupBy = GroupBy<NSManagedObject>("key1", "key2")
|
||||
XCTAssertEqual(groupBy, GroupBy("key1", "key2"))
|
||||
XCTAssertEqual(groupBy, GroupBy(["key1", "key2"]))
|
||||
XCTAssertNotEqual(groupBy, GroupBy("key2", "key1"))
|
||||
@@ -66,10 +67,10 @@ final class GroupByTests: BaseTestCase {
|
||||
|
||||
self.prepareStack { (dataStack) in
|
||||
|
||||
let groupBy = GroupBy(#keyPath(TestEntity1.testString))
|
||||
let groupBy = GroupBy<NSManagedObject>(#keyPath(TestEntity1.testString))
|
||||
|
||||
let request = CoreStoreFetchRequest()
|
||||
_ = From<TestEntity1>().applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
try From<TestEntity1>().applyToFetchRequest(request, context: dataStack.mainContext)
|
||||
groupBy.applyToFetchRequest(request)
|
||||
|
||||
XCTAssertNotNil(request.propertiesToGroupBy)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>6.2.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// IntoTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -36,7 +37,7 @@ final class IntoTests: XCTestCase {
|
||||
@objc
|
||||
dynamic func test_ThatIntoClauseConstants_AreCorrect() {
|
||||
|
||||
XCTAssertEqual(Into<NSManagedObject>.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME")
|
||||
XCTAssertEqual(DataStack.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME")
|
||||
}
|
||||
|
||||
@objc
|
||||
@@ -44,7 +45,7 @@ final class IntoTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let into = Into()
|
||||
let into = Into<NSManagedObject>()
|
||||
XCTAssert(into.entityClass === NSManagedObject.self)
|
||||
XCTAssertNil(into.configuration)
|
||||
XCTAssertTrue(into.inferStoreIfPossible)
|
||||
@@ -58,14 +59,7 @@ final class IntoTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>()
|
||||
XCTAssert(into.entityClass === TestEntity1.self)
|
||||
XCTAssertNil(into.configuration)
|
||||
XCTAssertTrue(into.inferStoreIfPossible)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass)
|
||||
let into = Into(TestEntity1.self)
|
||||
XCTAssert(into.entityClass === TestEntity1.self)
|
||||
XCTAssertNil(into.configuration)
|
||||
XCTAssertTrue(into.inferStoreIfPossible)
|
||||
@@ -84,13 +78,6 @@ final class IntoTests: XCTestCase {
|
||||
XCTAssertEqual(into.configuration, "Config1")
|
||||
XCTAssertFalse(into.inferStoreIfPossible)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass, "Config1")
|
||||
XCTAssert(into.entityClass === TestEntity1.self)
|
||||
XCTAssertEqual(into.configuration, "Config1")
|
||||
XCTAssertFalse(into.inferStoreIfPossible)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
@@ -98,43 +85,30 @@ final class IntoTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let into = Into()
|
||||
XCTAssertEqual(into, Into())
|
||||
let into = Into<NSManagedObject>()
|
||||
XCTAssertEqual(into, Into<NSManagedObject>())
|
||||
XCTAssertEqual(into, Into(NSManagedObject.self as AnyClass))
|
||||
XCTAssertFalse(into == Into<TestEntity1>())
|
||||
XCTAssertEqual(into, Into(NSManagedObject.self))
|
||||
XCTAssertNotEqual(into, Into<NSManagedObject>(TestEntity1.self))
|
||||
XCTAssertNotEqual(into, Into<NSManagedObject>("Config1"))
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>()
|
||||
XCTAssertEqual(into, Into<TestEntity1>())
|
||||
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass))
|
||||
XCTAssertFalse(into == Into<TestEntity2>())
|
||||
XCTAssertEqual(into, Into(TestEntity1.self))
|
||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>()
|
||||
XCTAssertEqual(into, Into<TestEntity1>())
|
||||
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass))
|
||||
XCTAssertFalse(into == Into<TestEntity2>())
|
||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass)
|
||||
let into = Into(TestEntity1.self)
|
||||
XCTAssert(into == Into<TestEntity1>())
|
||||
XCTAssertEqual(into, Into(TestEntity1.self))
|
||||
XCTAssertFalse(into == Into<TestEntity2>())
|
||||
XCTAssertFalse(into == Into<TestEntity1>("Config1"))
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>("Config1")
|
||||
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
||||
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass, "Config1"))
|
||||
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
|
||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||
}
|
||||
do {
|
||||
@@ -142,16 +116,14 @@ final class IntoTests: XCTestCase {
|
||||
let into = Into(TestEntity1.self, "Config1")
|
||||
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
||||
XCTAssertEqual(into, Into<TestEntity1>("Config1"))
|
||||
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
|
||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass, "Config1")
|
||||
XCTAssert(into == Into<TestEntity1>("Config1"))
|
||||
let into = Into(TestEntity1.self, "Config1")
|
||||
XCTAssertEqual(into, Into<TestEntity1>("Config1"))
|
||||
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
||||
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
|
||||
XCTAssertFalse(into == Into<TestEntity1>("Config2"))
|
||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,45 +132,9 @@ final class IntoTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let into = Into()
|
||||
let into = Into<NSManagedObject>()
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertEqual(into, objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>()
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertTrue(into == objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass)
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertEqual(into, objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass)
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertEqual(into, objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into<TestEntity1>("Config1")
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertTrue(into == objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self, "Config1")
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertTrue(into == objcInto.bridgeToSwift)
|
||||
}
|
||||
do {
|
||||
|
||||
let into = Into(TestEntity1.self as AnyClass, "Config1")
|
||||
let objcInto = into.bridgeToObjectiveC
|
||||
XCTAssertTrue(into == objcInto.bridgeToSwift)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// ListObserverTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,14 +23,13 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
||||
|
||||
// MARK: - ListObserverTests
|
||||
|
||||
class ListObserverTests: BaseTestDataTestCase {
|
||||
@@ -44,7 +43,7 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
let monitor = stack.monitorSectionedList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
monitor.addObserver(observer)
|
||||
|
||||
@@ -54,8 +53,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
|
||||
var events = 0
|
||||
|
||||
let willChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorWillChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -68,8 +67,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
let didInsertSectionExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didInsertSection:toSectionIndex:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -77,7 +76,7 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
[
|
||||
"sectionInfo": monitor.sectionInfoAtIndex(0),
|
||||
"sectionInfo": monitor.sectionInfo(at: 0),
|
||||
"sectionIndex": 0
|
||||
] as NSDictionary
|
||||
)
|
||||
@@ -88,8 +87,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 1
|
||||
}
|
||||
)
|
||||
let didInsertObjectExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didInsertObject:toIndexPath:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -103,8 +102,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
)
|
||||
|
||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||
XCTAssertEqual(indexPath?.section, 0)
|
||||
XCTAssertEqual(indexPath?.row, 0)
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
|
||||
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||
@@ -120,8 +119,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 2
|
||||
}
|
||||
)
|
||||
let didChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorDidChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -134,29 +133,29 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
object.testNumber = NSNumber(value: 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
transaction.commit { (result) in
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
object.testNumber = NSNumber(value: 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -172,21 +171,21 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
let monitor = stack.monitorSectionedList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
monitor.addObserver(observer)
|
||||
|
||||
XCTAssertTrue(monitor.hasSections())
|
||||
XCTAssertEqual(monitor.numberOfSections(), 2)
|
||||
XCTAssertTrue(monitor.hasObjects())
|
||||
XCTAssertTrue(monitor.hasObjectsInSection(0))
|
||||
XCTAssertEqual(monitor.numberOfObjectsInSection(0), 2)
|
||||
XCTAssertEqual(monitor.numberOfObjectsInSection(1), 3)
|
||||
XCTAssertTrue(monitor.hasObjects(in: 0))
|
||||
XCTAssertEqual(monitor.numberOfObjects(in: 0), 2)
|
||||
XCTAssertEqual(monitor.numberOfObjects(in: 1), 3)
|
||||
|
||||
var events = 0
|
||||
|
||||
let willChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorWillChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -199,62 +198,60 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
for _ in 1 ... 2 {
|
||||
|
||||
let didUpdateObjectExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didUpdateObject:atIndexPath:",
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
XCTAssert(events == 1 || events == 2)
|
||||
|
||||
let userInfo = note.userInfo
|
||||
XCTAssertNotNil(userInfo)
|
||||
XCTAssertEqual(
|
||||
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||
["indexPath", "object"]
|
||||
)
|
||||
|
||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
|
||||
switch object?.testEntityID {
|
||||
|
||||
XCTAssert(events == 1 || events == 2)
|
||||
case NSNumber(value: 101)?:
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 0), 1)
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
|
||||
|
||||
let userInfo = note.userInfo
|
||||
XCTAssertNotNil(userInfo)
|
||||
XCTAssertEqual(
|
||||
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||
["indexPath", "object"]
|
||||
)
|
||||
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 11))
|
||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "11"))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:11")
|
||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!)
|
||||
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!)
|
||||
|
||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
case NSNumber(value: 102)?:
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
|
||||
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
|
||||
|
||||
switch object?.testEntityID {
|
||||
|
||||
case NSNumber(value: 101)?:
|
||||
XCTAssertEqual(indexPath?.section, 1)
|
||||
XCTAssertEqual(indexPath?.row, 0)
|
||||
|
||||
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 11))
|
||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "11"))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:11")
|
||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!)
|
||||
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!)
|
||||
|
||||
case NSNumber(value: 102)?:
|
||||
XCTAssertEqual(indexPath?.section, 0)
|
||||
XCTAssertEqual(indexPath?.row, 0)
|
||||
|
||||
XCTAssertEqual(object?.testBoolean, NSNumber(value: false))
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 22))
|
||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "22"))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:22")
|
||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!)
|
||||
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!)
|
||||
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 1 || events == 2
|
||||
XCTAssertEqual(object?.testBoolean, NSNumber(value: false))
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 22))
|
||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "22"))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:22")
|
||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!)
|
||||
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!)
|
||||
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
}
|
||||
let didChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorDidChange:",
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 1 || events == 2
|
||||
}
|
||||
)
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -268,49 +265,49 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
object.testNumber = NSNumber(value: 11)
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 22)
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
object.testNumber = NSNumber(value: 11)
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 22)
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -326,14 +323,14 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
let monitor = stack.monitorSectionedList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
monitor.addObserver(observer)
|
||||
|
||||
var events = 0
|
||||
|
||||
let willChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorWillChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -346,8 +343,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
let didMoveObjectExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -361,12 +358,12 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
)
|
||||
|
||||
let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath
|
||||
XCTAssertEqual(fromIndexPath?.section, 0)
|
||||
XCTAssertEqual(fromIndexPath?.row, 0)
|
||||
XCTAssertEqual(fromIndexPath?.index(atPosition: 0), 0)
|
||||
XCTAssertEqual(fromIndexPath?.index(atPosition: 1), 0)
|
||||
|
||||
let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath
|
||||
XCTAssertEqual(toIndexPath?.section, 1)
|
||||
XCTAssertEqual(toIndexPath?.row, 1)
|
||||
XCTAssertEqual(toIndexPath?.index(atPosition: 0), 1)
|
||||
XCTAssertEqual(toIndexPath?.index(atPosition: 1), 1)
|
||||
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.testEntityID, NSNumber(value: 102))
|
||||
@@ -379,8 +376,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 1
|
||||
}
|
||||
)
|
||||
let didChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorDidChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -394,31 +391,31 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
if let object = transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
}
|
||||
else {
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -434,14 +431,14 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
let monitor = stack.monitorSectionedList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
monitor.addObserver(observer)
|
||||
|
||||
var events = 0
|
||||
|
||||
let willChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorWillChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -454,40 +451,37 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
for _ in 1 ... 2 {
|
||||
|
||||
let didUpdateObjectExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didDeleteObject:fromIndexPath:",
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
XCTAssert(events == 1 || events == 2)
|
||||
|
||||
let userInfo = note.userInfo
|
||||
XCTAssertNotNil(userInfo)
|
||||
XCTAssertEqual(
|
||||
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||
["indexPath", "object"]
|
||||
)
|
||||
|
||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||
|
||||
XCTAssertEqual(indexPath?.section, 0)
|
||||
XCTAssert(indexPath?.index(atPosition: 1) == 0 || indexPath?.index(atPosition: 1) == 1)
|
||||
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.isDeleted, true)
|
||||
|
||||
defer {
|
||||
|
||||
XCTAssert(events == 1 || events == 2)
|
||||
|
||||
let userInfo = note.userInfo
|
||||
XCTAssertNotNil(userInfo)
|
||||
XCTAssertEqual(
|
||||
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||
["indexPath", "object"]
|
||||
)
|
||||
|
||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||
|
||||
XCTAssertEqual(indexPath?.section, 0)
|
||||
XCTAssert(indexPath?.row == 0 || indexPath?.row == 1)
|
||||
|
||||
let object = userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.isDeleted, true)
|
||||
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 1 || events == 2
|
||||
events += 1
|
||||
}
|
||||
)
|
||||
}
|
||||
let didDeleteSectionExpectation = self.expectation(
|
||||
forNotification: "listMonitor:didDeleteSection:fromSectionIndex:",
|
||||
return events == 1 || events == 2
|
||||
}
|
||||
)
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -514,8 +508,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
return events == 3
|
||||
}
|
||||
)
|
||||
let didChangeExpectation = self.expectation(
|
||||
forNotification: "listMonitorDidChange:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -529,25 +523,26 @@ class ListObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
transaction.deleteAll(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||
)
|
||||
transaction.commit { (result) in
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
let count = try transaction.deleteAll(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||
)
|
||||
XCTAssertEqual(count, 2)
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -678,5 +673,3 @@ class TestListObserver: ListSectionObserver {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
304
CoreStoreTests/ListPublisherTests.swift
Normal file
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// ListPublisherTests.swift
|
||||
// CoreStore iOS
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - ListPublisherTests
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
class ListPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatListPublishers_CanReceiveInsertNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
let observer = NSObject()
|
||||
let listPublisher = stack.publishList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
XCTAssertFalse(listPublisher.snapshot.hasSections())
|
||||
XCTAssertFalse(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.itemIDs.isEmpty)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
listPublisher.addObserver(observer) { listPublisher in
|
||||
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 1)
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
object.testNumber = NSNumber(value: 1)
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
|
||||
withExtendedLifetime(listPublisher, {})
|
||||
withExtendedLifetime(observer, {})
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatListPublishers_CanReceiveUpdateNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
let observer = NSObject()
|
||||
let listPublisher = stack.publishList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2)
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
listPublisher.addObserver(observer) { listPublisher in
|
||||
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2)
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3)
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 11)
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testNumber = NSNumber(value: 22)
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
|
||||
withExtendedLifetime(listPublisher, {})
|
||||
withExtendedLifetime(observer, {})
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatListPublishers_CanReceiveMoveNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
let observer = NSObject()
|
||||
let listPublisher = stack.publishList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2)
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
listPublisher.addObserver(observer) { listPublisher in
|
||||
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 1)
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 4)
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
|
||||
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
|
||||
withExtendedLifetime(listPublisher, {})
|
||||
withExtendedLifetime(observer, {})
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatListPublishers_CanReceiveDeleteNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
let observer = NSObject()
|
||||
let listPublisher = stack.publishList(
|
||||
From<TestEntity1>(),
|
||||
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||
)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2)
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
listPublisher.addObserver(observer) { listPublisher in
|
||||
|
||||
XCTAssertTrue(listPublisher.snapshot.hasSections())
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfSections, 1)
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems())
|
||||
XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0))
|
||||
XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 3)
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
let count = try transaction.deleteAll(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||
)
|
||||
XCTAssertEqual(count, 2)
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// MigrationChainTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -37,8 +37,8 @@ final class MigrationChainTests: XCTestCase {
|
||||
dynamic func test_ThatNilMigrationChains_HaveNoVersions() {
|
||||
|
||||
let chain: MigrationChain = nil
|
||||
XCTAssertTrue(chain.valid)
|
||||
XCTAssertTrue(chain.empty)
|
||||
XCTAssertTrue(chain.isValid)
|
||||
XCTAssertTrue(chain.isEmpty)
|
||||
|
||||
XCTAssertFalse(chain.contains("version1"))
|
||||
XCTAssertNil(chain.nextVersionFrom("version1"))
|
||||
@@ -48,8 +48,8 @@ final class MigrationChainTests: XCTestCase {
|
||||
dynamic func test_ThatStringMigrationChains_HaveOneVersion() {
|
||||
|
||||
let chain: MigrationChain = "version1"
|
||||
XCTAssertTrue(chain.valid)
|
||||
XCTAssertTrue(chain.empty)
|
||||
XCTAssertTrue(chain.isValid)
|
||||
XCTAssertTrue(chain.isEmpty)
|
||||
|
||||
XCTAssertTrue(chain.contains("version1"))
|
||||
XCTAssertFalse(chain.contains("version2"))
|
||||
@@ -62,8 +62,8 @@ final class MigrationChainTests: XCTestCase {
|
||||
dynamic func test_ThatArrayMigrationChains_HaveLinearVersions() {
|
||||
|
||||
let chain: MigrationChain = ["version1", "version2", "version3", "version4"]
|
||||
XCTAssertTrue(chain.valid)
|
||||
XCTAssertFalse(chain.empty)
|
||||
XCTAssertTrue(chain.isValid)
|
||||
XCTAssertFalse(chain.isEmpty)
|
||||
|
||||
XCTAssertTrue(chain.contains("version1"))
|
||||
XCTAssertTrue(chain.contains("version2"))
|
||||
@@ -86,8 +86,8 @@ final class MigrationChainTests: XCTestCase {
|
||||
"version2": "version3",
|
||||
"version3": "version4"
|
||||
]
|
||||
XCTAssertTrue(chain.valid)
|
||||
XCTAssertFalse(chain.empty)
|
||||
XCTAssertTrue(chain.isValid)
|
||||
XCTAssertFalse(chain.isEmpty)
|
||||
|
||||
XCTAssertTrue(chain.contains("version1"))
|
||||
XCTAssertTrue(chain.contains("version2"))
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10174" systemVersion="15F34" minimumToolsVersion="Xcode 4.3">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="17G2307" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="TestEntity1AAA" representedClassName="CoreStoreTests.TestEntity1" syncable="YES">
|
||||
<attribute name="testBoolean" optional="YES" attributeType="Boolean" syncable="YES"/>
|
||||
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/>
|
||||
<attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/>
|
||||
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" syncable="YES"/>
|
||||
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="testNumber" optional="YES" attributeType="Integer 32" syncable="YES"/>
|
||||
<attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<relationship name="testToManyUnordered" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToOne" inverseEntity="TestEntity1AAA" syncable="YES"/>
|
||||
<relationship name="testToOne" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToManyUnordered" inverseEntity="TestEntity1AAA" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES">
|
||||
<attribute name="testBoolean" optional="YES" attributeType="Boolean" syncable="YES"/>
|
||||
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/>
|
||||
<attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/>
|
||||
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" syncable="YES"/>
|
||||
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="testNumber" optional="YES" attributeType="Integer 32" syncable="YES"/>
|
||||
<attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<relationship name="testToManyOrdered" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TestEntity2" inverseName="testToOne" inverseEntity="TestEntity2" syncable="YES"/>
|
||||
<relationship name="testToOne" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TestEntity2" inverseName="testToManyOrdered" inverseEntity="TestEntity2" syncable="YES"/>
|
||||
</entity>
|
||||
<configuration name="Config1">
|
||||
<memberEntity name="TestEntity1AAA"/>
|
||||
@@ -27,7 +31,7 @@
|
||||
<memberEntity name="TestEntity2"/>
|
||||
</configuration>
|
||||
<elements>
|
||||
<element name="TestEntity1AAA" positionX="-63" positionY="-18" width="128" height="165"/>
|
||||
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="165"/>
|
||||
<element name="TestEntity1AAA" positionX="-63" positionY="-18" width="128" height="195"/>
|
||||
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="195"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -2,7 +2,7 @@
|
||||
// ObjectObserverTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,8 +29,6 @@ import XCTest
|
||||
import CoreStore
|
||||
|
||||
|
||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
||||
|
||||
// MARK: - ObjectObserverTests
|
||||
|
||||
class ObjectObserverTests: BaseTestDataTestCase {
|
||||
@@ -42,9 +40,9 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
guard let object = stack.fetchOne(
|
||||
guard let object = try stack.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
@@ -58,8 +56,8 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
var events = 0
|
||||
|
||||
let willUpdateExpectation = self.expectation(
|
||||
forNotification: "objectMonitor:willUpdateObject:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -75,8 +73,8 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
let didUpdateExpectation = self.expectation(
|
||||
forNotification: "objectMonitor:didUpdateObject:changedPersistentKeys:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -105,29 +103,29 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
object.testNumber = NSNumber(value: 10)
|
||||
object.testString = "nil:TestEntity1:10"
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
object.testNumber = NSNumber(value: 10)
|
||||
object.testString = "nil:TestEntity1:10"
|
||||
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -139,9 +137,9 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
guard let object = stack.fetchOne(
|
||||
guard let object = try stack.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
@@ -155,8 +153,8 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
var events = 0
|
||||
|
||||
let didDeleteExpectation = self.expectation(
|
||||
forNotification: "objectMonitor:didDeleteObject:",
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
@@ -173,29 +171,29 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.beginAsynchronous { (transaction) in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
transaction.delete(object)
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
XCTAssertTrue(monitor.isObjectDeleted)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
transaction.delete(object)
|
||||
|
||||
transaction.commit { (result) in
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let hasChanges):
|
||||
XCTAssertTrue(hasChanges)
|
||||
XCTAssertTrue(monitor.isObjectDeleted)
|
||||
saveExpectation.fulfill()
|
||||
|
||||
case .failure:
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
}
|
||||
}
|
||||
@@ -219,7 +217,7 @@ class TestObjectObserver: ObjectObserver {
|
||||
)
|
||||
}
|
||||
|
||||
func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, didUpdateObject object: TestEntity1, changedPersistentKeys: Set<KeyPath>) {
|
||||
func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, didUpdateObject object: TestEntity1, changedPersistentKeys: Set<String>) {
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
|
||||
@@ -242,5 +240,3 @@ class TestObjectObserver: ObjectObserver {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
154
CoreStoreTests/ObjectPublisherTests.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// ObjectPublisherTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - ObjectPublisherTests
|
||||
|
||||
class ObjectPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatObjectPublishers_CanReceiveUpdateNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
guard let object = try stack.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
let observer = NSObject()
|
||||
let objectPublisher = stack.publishObject(object)
|
||||
XCTAssertEqual(objectPublisher.object, object)
|
||||
XCTAssertNotNil(objectPublisher.snapshot)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
objectPublisher.addObserver(observer) { objectPublisher in
|
||||
|
||||
XCTAssertEqual(objectPublisher.object?.testNumber, NSNumber(value: 10))
|
||||
XCTAssertEqual(objectPublisher.object?.testString, "nil:TestEntity1:10")
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
object.testNumber = NSNumber(value: 10)
|
||||
object.testString = "nil:TestEntity1:10"
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
self.waitAndCheckExpectations()
|
||||
|
||||
withExtendedLifetime(objectPublisher, {})
|
||||
withExtendedLifetime(observer, {})
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatObjectPublishers_CanReceiveDeleteNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
self.prepareTestDataForStack(stack)
|
||||
|
||||
guard let object = try stack.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
let observer = NSObject()
|
||||
let objectPublisher = stack.publishObject(object)
|
||||
XCTAssertEqual(objectPublisher.object, object)
|
||||
XCTAssertNotNil(objectPublisher.snapshot)
|
||||
|
||||
let didChangeExpectation = self.expectation(description: "didChange")
|
||||
objectPublisher.addObserver(observer) { objectPublisher in
|
||||
|
||||
XCTAssertNil(objectPublisher.object)
|
||||
XCTAssertNil(objectPublisher.snapshot)
|
||||
|
||||
didChangeExpectation.fulfill()
|
||||
}
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
XCTFail()
|
||||
try transaction.cancel()
|
||||
}
|
||||
transaction.delete(object)
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
success: { (hasChanges) in
|
||||
|
||||
XCTAssertTrue(hasChanges)
|
||||
saveExpectation.fulfill()
|
||||
},
|
||||
failure: { _ in
|
||||
|
||||
XCTFail()
|
||||
}
|
||||
)
|
||||
|
||||
self.waitAndCheckExpectations()
|
||||
|
||||
withExtendedLifetime(objectPublisher, {})
|
||||
withExtendedLifetime(observer, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// OrderByTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -38,21 +39,21 @@ final class OrderByTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let orderBy = OrderBy()
|
||||
XCTAssertEqual(orderBy, OrderBy([NSSortDescriptor]()))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key", ascending: false)))
|
||||
let orderBy = OrderBy<NSManagedObject>()
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([NSSortDescriptor]()))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key", ascending: false)))
|
||||
XCTAssertTrue(orderBy.sortDescriptors.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
||||
let orderBy = OrderBy(sortDescriptor)
|
||||
let orderBy = OrderBy<NSManagedObject>(sortDescriptor)
|
||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
|
||||
XCTAssertEqual(orderBy, OrderBy(.ascending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.descending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key1", ascending: false)))
|
||||
XCTAssertEqual(orderBy, OrderBy([sortDescriptor]))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key1", ascending: false)))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
|
||||
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
||||
}
|
||||
do {
|
||||
@@ -61,76 +62,76 @@ final class OrderByTests: XCTestCase {
|
||||
NSSortDescriptor(key: "key1", ascending: true),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
let orderBy = OrderBy(sortDescriptors)
|
||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2")))
|
||||
let orderBy = OrderBy<NSManagedObject>(sortDescriptors)
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertNotEqual(
|
||||
orderBy,
|
||||
OrderBy(
|
||||
OrderBy<NSManagedObject>(
|
||||
[
|
||||
NSSortDescriptor(key: "key1", ascending: false),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
)
|
||||
)
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||
}
|
||||
do {
|
||||
|
||||
let orderBy = OrderBy(.ascending("key1"))
|
||||
let orderBy = OrderBy<NSManagedObject>(.ascending("key1"))
|
||||
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
|
||||
XCTAssertEqual(orderBy, OrderBy(.ascending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.descending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2")))
|
||||
XCTAssertEqual(orderBy, OrderBy([sortDescriptor]))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptor))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
|
||||
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
||||
}
|
||||
do {
|
||||
|
||||
let orderBy = OrderBy(.ascending("key1"), .descending("key2"))
|
||||
let orderBy = OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"))
|
||||
let sortDescriptors = [
|
||||
NSSortDescriptor(key: "key1", ascending: true),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertNotEqual(
|
||||
orderBy,
|
||||
OrderBy(
|
||||
OrderBy<NSManagedObject>(
|
||||
[
|
||||
NSSortDescriptor(key: "key1", ascending: false),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
)
|
||||
)
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||
}
|
||||
do {
|
||||
|
||||
let sortKeys: [SortKey] = [.ascending("key1"), .descending("key2")]
|
||||
let orderBy = OrderBy(sortKeys)
|
||||
let sortKeys: [OrderBy<NSManagedObject>.SortKey] = [.ascending("key1"), .descending("key2")]
|
||||
let orderBy = OrderBy<NSManagedObject>(sortKeys)
|
||||
let sortDescriptors = [
|
||||
NSSortDescriptor(key: "key1", ascending: true),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertNotEqual(
|
||||
orderBy,
|
||||
OrderBy(
|
||||
OrderBy<NSManagedObject>(
|
||||
[
|
||||
NSSortDescriptor(key: "key1", ascending: false),
|
||||
NSSortDescriptor(key: "key2", ascending: false)
|
||||
]
|
||||
)
|
||||
)
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||
}
|
||||
}
|
||||
@@ -138,15 +139,15 @@ final class OrderByTests: XCTestCase {
|
||||
@objc
|
||||
dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() {
|
||||
|
||||
let orderBy1 = OrderBy(.ascending("key1"))
|
||||
let orderBy2 = OrderBy(.descending("key2"))
|
||||
let orderBy3 = OrderBy(.ascending("key3"))
|
||||
let orderBy1 = OrderBy<NSManagedObject>(.ascending("key1"))
|
||||
let orderBy2 = OrderBy<NSManagedObject>(.descending("key2"))
|
||||
let orderBy3 = OrderBy<NSManagedObject>(.ascending("key3"))
|
||||
|
||||
do {
|
||||
|
||||
let plusOrderBy = orderBy1 + orderBy2 + orderBy3
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"), .ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2"), .ascending("key3")))
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
||||
@@ -158,14 +159,14 @@ final class OrderByTests: XCTestCase {
|
||||
|
||||
var plusOrderBy = orderBy1
|
||||
plusOrderBy += orderBy2
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2")))
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1)
|
||||
XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors)
|
||||
|
||||
plusOrderBy += orderBy3
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2")) + OrderBy(.ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")) + OrderBy<NSManagedObject>(.ascending("key3")))
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
||||
@@ -178,8 +179,8 @@ final class OrderByTests: XCTestCase {
|
||||
@objc
|
||||
dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() {
|
||||
|
||||
let orderBy = OrderBy(.ascending("key"))
|
||||
let request = CoreStoreFetchRequest()
|
||||
let orderBy = OrderBy<NSManagedObject>(.ascending("key"))
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
orderBy.applyToFetchRequest(request)
|
||||
XCTAssertNotNil(request.sortDescriptors)
|
||||
XCTAssertEqual(request.sortDescriptors ?? [], orderBy.sortDescriptors)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SectionByTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,14 +23,13 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
||||
|
||||
//MARK: - SectionByTests
|
||||
|
||||
final class SectionByTests: XCTestCase {
|
||||
@@ -40,18 +39,19 @@ final class SectionByTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let sectionBy = SectionBy("key")
|
||||
let sectionBy = SectionBy<NSManagedObject>("key")
|
||||
XCTAssertEqual(sectionBy.sectionKeyPath, "key")
|
||||
XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key")
|
||||
XCTAssertNil(sectionBy.sectionIndexTransformer("key"))
|
||||
}
|
||||
do {
|
||||
|
||||
let sectionBy = SectionBy("key") { $0.flatMap { "\($0):suffix" } }
|
||||
let sectionBy = SectionBy<NSManagedObject>(
|
||||
"key",
|
||||
sectionIndexTransformer: { $0.flatMap { "\($0):suffix" } }
|
||||
)
|
||||
XCTAssertEqual(sectionBy.sectionKeyPath, "key")
|
||||
XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key:suffix")
|
||||
XCTAssertNil(sectionBy.sectionIndexTransformer(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SelectTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -38,7 +39,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term: SelectTerm = "attribute"
|
||||
let term: SelectTerm<NSManagedObject> = "attribute"
|
||||
XCTAssertEqual(term, SelectTerm.attribute("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||
@@ -58,7 +59,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.attribute("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.attribute("attribute")
|
||||
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||
@@ -82,7 +83,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.average("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.average("attribute")
|
||||
XCTAssertEqual(term, SelectTerm.average("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
|
||||
@@ -106,7 +107,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.average("attribute", as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.average("attribute", as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.average("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
|
||||
@@ -135,7 +136,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.count("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.count("attribute")
|
||||
XCTAssertEqual(term, SelectTerm.count("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
|
||||
@@ -159,7 +160,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.count("attribute", as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.count("attribute", as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.count("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
|
||||
@@ -188,7 +189,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.maximum("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.maximum("attribute")
|
||||
XCTAssertEqual(term, SelectTerm.maximum("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
|
||||
@@ -212,7 +213,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.maximum("attribute", as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.maximum("attribute", as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.maximum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
|
||||
@@ -241,7 +242,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.minimum("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.minimum("attribute")
|
||||
XCTAssertEqual(term, SelectTerm.minimum("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
|
||||
@@ -265,7 +266,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.minimum("attribute", as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.minimum("attribute", as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.minimum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
|
||||
@@ -294,7 +295,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.sum("attribute")
|
||||
let term = SelectTerm<NSManagedObject>.sum("attribute")
|
||||
XCTAssertEqual(term, SelectTerm.sum("attribute"))
|
||||
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
|
||||
@@ -318,7 +319,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.sum("attribute", as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.sum("attribute", as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.sum("attribute", as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
|
||||
@@ -347,7 +348,7 @@ final class SelectTests: XCTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let term = SelectTerm.objectID()
|
||||
let term = SelectTerm<NSManagedObject>.objectID()
|
||||
XCTAssertEqual(term, SelectTerm.objectID())
|
||||
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||
@@ -368,7 +369,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let term = SelectTerm.objectID(as: "alias")
|
||||
let term = SelectTerm<NSManagedObject>.objectID(as: "alias")
|
||||
XCTAssertEqual(term, SelectTerm.objectID(as: "alias"))
|
||||
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias2"))
|
||||
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||
@@ -393,12 +394,12 @@ final class SelectTests: XCTestCase {
|
||||
@objc
|
||||
dynamic func test_ThatSelectClauses_ConfigureCorrectly() {
|
||||
|
||||
let term1 = SelectTerm.attribute("attribute1")
|
||||
let term2 = SelectTerm.attribute("attribute2")
|
||||
let term3 = SelectTerm.attribute("attribute3")
|
||||
let term1 = SelectTerm<NSManagedObject>.attribute("attribute1")
|
||||
let term2 = SelectTerm<NSManagedObject>.attribute("attribute2")
|
||||
let term3 = SelectTerm<NSManagedObject>.attribute("attribute3")
|
||||
do {
|
||||
|
||||
let select = Select<Int>(term1, term2, term3)
|
||||
let select = Select<NSManagedObject, Int>(term1, term2, term3)
|
||||
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
||||
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
||||
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
||||
@@ -408,7 +409,7 @@ final class SelectTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let select = Select<Int>([term1, term2, term3])
|
||||
let select = Select<NSManagedObject, Int>([term1, term2, term3])
|
||||
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
||||
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
||||
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SetupTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,9 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
@@ -36,10 +39,14 @@ class SetupTests: BaseTestDataTestCase {
|
||||
|
||||
do {
|
||||
|
||||
let model = NSManagedObjectModel.mergedModel(from: [Bundle(for: type(of: self))])!
|
||||
|
||||
let stack = DataStack(model: model, migrationChain: nil)
|
||||
XCTAssertEqual(stack.coordinator.managedObjectModel, model)
|
||||
let schemaHistory = SchemaHistory(
|
||||
XcodeDataModelSchema.from(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
)
|
||||
let stack = DataStack(schemaHistory: schemaHistory)
|
||||
XCTAssertEqual(stack.coordinator.managedObjectModel, schemaHistory.rawModel)
|
||||
XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator)
|
||||
XCTAssertNil(stack.rootSavingContext.parent)
|
||||
XCTAssertFalse(stack.rootSavingContext.isDataStackContext)
|
||||
@@ -47,32 +54,26 @@ class SetupTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(stack.mainContext.parent, stack.rootSavingContext)
|
||||
XCTAssertTrue(stack.mainContext.isDataStackContext)
|
||||
XCTAssertFalse(stack.mainContext.isTransactionContext)
|
||||
XCTAssertEqual(stack.model, model)
|
||||
XCTAssertTrue(stack.migrationChain.valid)
|
||||
XCTAssertTrue(stack.migrationChain.empty)
|
||||
XCTAssertTrue(stack.migrationChain.rootVersions.isEmpty)
|
||||
XCTAssertTrue(stack.migrationChain.leafVersions.isEmpty)
|
||||
|
||||
CoreStore.defaultStack = stack
|
||||
XCTAssertEqual(CoreStore.defaultStack, stack)
|
||||
XCTAssertEqual(stack.schemaHistory.rawModel, schemaHistory.rawModel)
|
||||
XCTAssertTrue(stack.schemaHistory.migrationChain.isValid)
|
||||
XCTAssertTrue(stack.schemaHistory.migrationChain.isEmpty)
|
||||
XCTAssertTrue(stack.schemaHistory.migrationChain.rootVersions.isEmpty)
|
||||
XCTAssertTrue(stack.schemaHistory.migrationChain.leafVersions.isEmpty)
|
||||
}
|
||||
do {
|
||||
|
||||
let migrationChain: MigrationChain = ["version1", "version2", "version3"]
|
||||
let migrationChain: MigrationChain = ["version1", "version2", "version3", "Model"]
|
||||
|
||||
let stack = self.expectLogger([.logWarning]) {
|
||||
|
||||
DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self)),
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self),
|
||||
migrationChain: migrationChain
|
||||
)
|
||||
}
|
||||
XCTAssertEqual(stack.modelVersion, "Model")
|
||||
XCTAssertEqual(stack.migrationChain, migrationChain)
|
||||
|
||||
CoreStore.defaultStack = stack
|
||||
XCTAssertEqual(CoreStore.defaultStack, stack)
|
||||
XCTAssertEqual(stack.schemaHistory.migrationChain, migrationChain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +81,8 @@ class SetupTests: BaseTestDataTestCase {
|
||||
dynamic func test_ThatInMemoryStores_SetupCorrectly() {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
do {
|
||||
|
||||
@@ -135,8 +136,8 @@ class SetupTests: BaseTestDataTestCase {
|
||||
dynamic func test_ThatSQLiteStores_SetupCorrectly() {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
do {
|
||||
|
||||
@@ -203,14 +204,13 @@ class SetupTests: BaseTestDataTestCase {
|
||||
do {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
try! stack.addStorageAndWait(sqliteStore)
|
||||
self.prepareTestDataForStack(stack)
|
||||
}
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
|
||||
return try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||
@@ -223,10 +223,13 @@ class SetupTests: BaseTestDataTestCase {
|
||||
|
||||
let metadata = try createStore()
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
try sqliteStore.cs_eraseStorageAndWait(
|
||||
metadata: metadata,
|
||||
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
|
||||
)
|
||||
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: stack.model[metadata])
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
@@ -238,7 +241,7 @@ class SetupTests: BaseTestDataTestCase {
|
||||
do {
|
||||
|
||||
let metadata = try createStore()
|
||||
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
|
||||
try sqliteStore.cs_eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
@@ -253,12 +256,12 @@ class SetupTests: BaseTestDataTestCase {
|
||||
dynamic func test_ThatLegacySQLiteStores_SetupCorrectly() {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
do {
|
||||
|
||||
let sqliteStore = LegacySQLiteStore()
|
||||
let sqliteStore = SQLiteStore.legacy()
|
||||
do {
|
||||
|
||||
try stack.addStorageAndWait(sqliteStore)
|
||||
@@ -273,7 +276,7 @@ class SetupTests: BaseTestDataTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let sqliteStore = LegacySQLiteStore(
|
||||
let sqliteStore = SQLiteStore.legacy(
|
||||
fileName: "ConfigStore1.sqlite",
|
||||
configuration: "Config1",
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
@@ -292,7 +295,7 @@ class SetupTests: BaseTestDataTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let sqliteStore = LegacySQLiteStore(
|
||||
let sqliteStore = SQLiteStore.legacy(
|
||||
fileName: "ConfigStore2.sqlite",
|
||||
configuration: "Config2",
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
@@ -315,20 +318,26 @@ class SetupTests: BaseTestDataTestCase {
|
||||
dynamic func test_ThatLegacySQLiteStores_DeleteFilesCorrectly() {
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let sqliteStore = LegacySQLiteStore()
|
||||
let sqliteStore = SQLiteStore.legacy()
|
||||
func createStore() throws -> [String: Any] {
|
||||
|
||||
do {
|
||||
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
try! stack.addStorageAndWait(
|
||||
SQLiteStore.legacy(
|
||||
fileName: sqliteStore.fileURL.lastPathComponent,
|
||||
configuration: sqliteStore.configuration,
|
||||
migrationMappingProviders: sqliteStore.migrationMappingProviders,
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
try! stack.addStorageAndWait(sqliteStore)
|
||||
self.prepareTestDataForStack(stack)
|
||||
}
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
|
||||
return try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||
@@ -341,10 +350,13 @@ class SetupTests: BaseTestDataTestCase {
|
||||
|
||||
let metadata = try createStore()
|
||||
let stack = DataStack(
|
||||
modelName: "Model",
|
||||
bundle: Bundle(for: type(of: self))
|
||||
xcodeModelName: "Model",
|
||||
bundle: Bundle(for: Self.self)
|
||||
)
|
||||
try sqliteStore.cs_eraseStorageAndWait(
|
||||
metadata: metadata,
|
||||
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
|
||||
)
|
||||
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: stack.model[metadata])
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
@@ -356,7 +368,7 @@ class SetupTests: BaseTestDataTestCase {
|
||||
do {
|
||||
|
||||
let metadata = try createStore()
|
||||
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
|
||||
try sqliteStore.cs_eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// StorageInterfaceTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -83,33 +84,44 @@ final class StorageInterfaceTests: XCTestCase {
|
||||
let store = SQLiteStore()
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertNil(store.configuration)
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
XCTAssertEqual(
|
||||
store.storeOptions as NSDictionary?,
|
||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||
)
|
||||
|
||||
XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL)
|
||||
XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles)
|
||||
XCTAssertTrue(store.migrationMappingProviders.isEmpty)
|
||||
XCTAssertEqual(store.localStorageOptions, .none)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatFileURLSQLiteStores_ConfigureCorrectly() {
|
||||
|
||||
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent(NSUUID().uuidString, isDirectory: false)!
|
||||
let fileURL = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent(UUID().uuidString, isDirectory: false)
|
||||
.appendingPathExtension("db")
|
||||
let bundles = [Bundle(for: type(of: self))]
|
||||
let mappingProvider = XcodeSchemaMappingProvider(
|
||||
from: "V1", to: "V2",
|
||||
mappingModelBundle: Bundle(for: Self.self)
|
||||
)
|
||||
|
||||
let store = SQLiteStore(
|
||||
fileURL: fileURL,
|
||||
configuration: "config1",
|
||||
mappingModelBundles: bundles,
|
||||
migrationMappingProviders: [mappingProvider],
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertEqual(store.configuration, "config1")
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
XCTAssertEqual(
|
||||
store.storeOptions as NSDictionary?,
|
||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||
)
|
||||
|
||||
XCTAssertEqual(store.fileURL, fileURL)
|
||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
||||
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||
}
|
||||
|
||||
@@ -117,21 +129,27 @@ final class StorageInterfaceTests: XCTestCase {
|
||||
dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() {
|
||||
|
||||
let fileName = UUID().uuidString + ".db"
|
||||
let bundles = [Bundle(for: type(of: self))]
|
||||
|
||||
let mappingProvider = XcodeSchemaMappingProvider(
|
||||
from: "V1", to: "V2",
|
||||
mappingModelBundle: Bundle(for: Self.self)
|
||||
)
|
||||
let store = SQLiteStore(
|
||||
fileName: fileName,
|
||||
configuration: "config1",
|
||||
mappingModelBundles: bundles,
|
||||
migrationMappingProviders: [mappingProvider],
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertEqual(store.configuration, "config1")
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
XCTAssertEqual(
|
||||
store.storeOptions as NSDictionary?,
|
||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||
)
|
||||
|
||||
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.defaultRootDirectory)
|
||||
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
||||
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||
}
|
||||
|
||||
@@ -152,65 +170,52 @@ final class StorageInterfaceTests: XCTestCase {
|
||||
.appendingPathComponent(DataStack.applicationName, isDirectory: false)
|
||||
.appendingPathExtension("sqlite")
|
||||
|
||||
XCTAssertEqual(LegacySQLiteStore.defaultRootDirectory, legacyDefaultRootDirectory)
|
||||
XCTAssertEqual(LegacySQLiteStore.defaultFileURL, legacyDefaultFileURL)
|
||||
XCTAssertEqual(SQLiteStore.legacyDefaultRootDirectory, legacyDefaultRootDirectory)
|
||||
XCTAssertEqual(SQLiteStore.legacyDefaultFileURL, legacyDefaultFileURL)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() {
|
||||
|
||||
let store = LegacySQLiteStore()
|
||||
let store = SQLiteStore.legacy()
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertNil(store.configuration)
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
|
||||
XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL)
|
||||
XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles)
|
||||
XCTAssertEqual(store.localStorageOptions, .none)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatFileURLLegacySQLiteStores_ConfigureCorrectly() {
|
||||
|
||||
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent(NSUUID().uuidString, isDirectory: false)!
|
||||
.appendingPathExtension("db")
|
||||
let bundles = [Bundle(for: type(of: self))]
|
||||
|
||||
let store = LegacySQLiteStore(
|
||||
fileURL: fileURL,
|
||||
configuration: "config1",
|
||||
mappingModelBundles: bundles,
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
XCTAssertEqual(
|
||||
store.storeOptions as NSDictionary?,
|
||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||
)
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertEqual(store.configuration, "config1")
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
|
||||
XCTAssertEqual(store.fileURL, fileURL)
|
||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
||||
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||
XCTAssertEqual(store.fileURL, SQLiteStore.legacyDefaultFileURL)
|
||||
XCTAssertTrue(store.migrationMappingProviders.isEmpty)
|
||||
XCTAssertEqual(store.localStorageOptions, .none)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() {
|
||||
|
||||
let fileName = UUID().uuidString + ".db"
|
||||
let bundles = [Bundle(for: type(of: self))]
|
||||
|
||||
let store = LegacySQLiteStore(
|
||||
let mappingProvider = XcodeSchemaMappingProvider(
|
||||
from: "V1", to: "V2",
|
||||
mappingModelBundle: Bundle(for: Self.self)
|
||||
)
|
||||
let store = SQLiteStore.legacy(
|
||||
fileName: fileName,
|
||||
configuration: "config1",
|
||||
mappingModelBundles: bundles,
|
||||
migrationMappingProviders: [mappingProvider],
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||
XCTAssertEqual(store.configuration, "config1")
|
||||
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
||||
XCTAssertEqual(
|
||||
store.storeOptions as NSDictionary?,
|
||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||
)
|
||||
|
||||
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), LegacySQLiteStore.defaultRootDirectory)
|
||||
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.legacyDefaultRootDirectory)
|
||||
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
||||
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// TestEntity1.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2014 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,4 +36,6 @@ class TestEntity1: NSManagedObject {
|
||||
@NSManaged var testDecimal: NSDecimalNumber?
|
||||
@NSManaged var testData: Data?
|
||||
@NSManaged var testNil: String?
|
||||
@NSManaged var testToOne: TestEntity1?
|
||||
@NSManaged var testToManyUnordered: NSSet?
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// TestEntity1.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2014 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,4 +36,6 @@ class TestEntity2: NSManagedObject {
|
||||
@NSManaged var testDecimal: NSDecimalNumber?
|
||||
@NSManaged var testData: Data?
|
||||
@NSManaged var testNil: String?
|
||||
@NSManaged var testToOne: TestEntity2?
|
||||
@NSManaged var testToManyOrdered: NSOrderedSet?
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// TweakTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
@@ -43,7 +44,7 @@ final class TweakTests: XCTestCase {
|
||||
$0.fetchLimit = 200
|
||||
$0.predicate = predicate
|
||||
}
|
||||
let request = CoreStoreFetchRequest()
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
tweak.applyToFetchRequest(request)
|
||||
XCTAssertEqual(request.fetchOffset, 100)
|
||||
XCTAssertEqual(request.fetchLimit, 200)
|
||||
|
||||
55
CoreStoreTests/VersionLockTests.swift
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// VersionLockTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
//MARK: - VersionLockTests
|
||||
|
||||
final class VersionLockTests: XCTestCase {
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatVersionLocksProduceCorrectHashes() {
|
||||
|
||||
let versionLock: VersionLock = [
|
||||
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
|
||||
"Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7],
|
||||
"Person": [0x2831cf046084d96d, 0xbe19b13ace54641, 0x635a082728b0f6f0, 0x3d4ef2dd4b74a87c]
|
||||
]
|
||||
XCTAssertEqual(
|
||||
versionLock.description,
|
||||
"""
|
||||
[
|
||||
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
|
||||
"Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7],
|
||||
"Person": [0x2831cf046084d96d, 0xbe19b13ace54641, 0x635a082728b0f6f0, 0x3d4ef2dd4b74a87c]
|
||||
]
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// WhereTests.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2016 John Rommel Estropia
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,67 +23,373 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import XCTest
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - XCTAssertAllEqual
|
||||
|
||||
private func XCTAssertAllEqual<O>(_ whereClauses: Where<O>...) {
|
||||
|
||||
XCTAssertAllEqual(whereClauses)
|
||||
}
|
||||
|
||||
private func XCTAssertAllEqual<O>(_ whereClauses: [Where<O>]) {
|
||||
|
||||
for i in whereClauses.indices {
|
||||
|
||||
for j in whereClauses.indices where j != i {
|
||||
|
||||
XCTAssertEqual(whereClauses[i], whereClauses[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func XCTAssertAllEqual<D: Equatable>(_ items: D...) {
|
||||
|
||||
for i in items.indices {
|
||||
|
||||
for j in items.indices where j != i {
|
||||
|
||||
XCTAssertEqual(items[i], items[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - WhereTests
|
||||
|
||||
final class WhereTests: XCTestCase {
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
|
||||
|
||||
XCTAssertAllEqual(String(keyPath: \TestEntity1.testEntityID), "testEntityID")
|
||||
XCTAssertAllEqual(String(keyPath: \Animal.$color), "color")
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatExpressions_HaveCorrectKeyPaths() {
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
#keyPath(TestEntity1.testToOne.testEntityID),
|
||||
(\TestEntity1.testToOne ~ \.testEntityID).description,
|
||||
String(keyPath: \TestEntity1.testToOne ~ \.testEntityID)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
#keyPath(TestEntity1.testToOne.testToOne.testToManyUnordered),
|
||||
(\TestEntity1.testToOne ~ \.testToOne ~ \.testToManyUnordered).description,
|
||||
String(keyPath: \TestEntity1.testToOne ~ \.testToOne ~ \.testToManyUnordered)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
#keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
|
||||
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).description,
|
||||
String(keyPath: \TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered)
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"master.pets",
|
||||
(\Animal.$master ~ \.$pets).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.species",
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets ~ \.$species)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.master",
|
||||
(\Animal.$master ~ \.$pets ~ \.$master).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets ~ \.$master)
|
||||
)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
#keyPath(TestEntity1.testToOne.testToManyUnordered) + ".@count",
|
||||
(\TestEntity1.testToOne ~ \.testToManyUnordered).count().description,
|
||||
String(keyPath: (\TestEntity1.testToOne ~ \.testToManyUnordered).count())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
#keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered) + ".@count",
|
||||
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).count().description,
|
||||
String(keyPath: (\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).count())
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.@count",
|
||||
(\Animal.$master ~ \.$pets).count().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).count())
|
||||
)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ANY " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
|
||||
(\TestEntity1.testToOne ~ \.testToManyUnordered).any().description,
|
||||
String(keyPath: (\TestEntity1.testToOne ~ \.testToManyUnordered).any())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ANY " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
|
||||
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).any().description,
|
||||
String(keyPath: (\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).any())
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ANY master.pets",
|
||||
(\Animal.$master ~ \.$pets).any().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).any())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ANY master.pets.species",
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).any().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).any())
|
||||
)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ALL " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
|
||||
(\TestEntity1.testToOne ~ \.testToManyUnordered).all().description,
|
||||
String(keyPath: (\TestEntity1.testToOne ~ \.testToManyUnordered).all())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ALL " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
|
||||
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).all().description,
|
||||
String(keyPath: (\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).all())
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ALL master.pets",
|
||||
(\Animal.$master ~ \.$pets).all().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).all())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ALL master.pets.species",
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).all().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).all())
|
||||
)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"NONE " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
|
||||
(\TestEntity1.testToOne ~ \.testToManyUnordered).none().description,
|
||||
String(keyPath: (\TestEntity1.testToOne ~ \.testToManyUnordered).none())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"NONE " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
|
||||
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).none().description,
|
||||
String(keyPath: (\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).none())
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"NONE master.pets",
|
||||
(\Animal.$master ~ \.$pets).none().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).none())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"NONE master.pets.species",
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).none().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).none())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatWhereClauses_CanBeCreatedFromExpressionsCorrectly() {
|
||||
|
||||
do {
|
||||
|
||||
let dummy = "dummy"
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testString) == dummy
|
||||
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testString)) == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$name) == dummy
|
||||
let predicate = NSPredicate(format: "master.name == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
let dummy = "dummy"
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToOne ~ \.testString) == dummy
|
||||
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testToOne.testString)) == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$spouse ~ \.$name) == dummy
|
||||
let predicate = NSPredicate(format: "master.spouse.name == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
let count = 3
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered).count() == count
|
||||
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testToManyUnordered)).@count == %d", count)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets).count() == count
|
||||
let predicate = NSPredicate(format: "master.pets.@count == %d", count)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
let dummy = "dummy"
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered ~ \TestEntity1.testString).any() == dummy
|
||||
let predicate = NSPredicate(format: "ANY \(#keyPath(TestEntity1.testToOne.testToManyUnordered)).\(#keyPath(TestEntity1.testString)) == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).any() == dummy
|
||||
let predicate = NSPredicate(format: "ANY master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
let dummy = "dummy"
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered ~ \TestEntity1.testString).all() == dummy
|
||||
let predicate = NSPredicate(format: "ALL \(#keyPath(TestEntity1.testToOne.testToManyUnordered)).\(#keyPath(TestEntity1.testString)) == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).all() == dummy
|
||||
let predicate = NSPredicate(format: "ALL master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
let dummy = "dummy"
|
||||
do {
|
||||
|
||||
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered ~ \TestEntity1.testString).none() == dummy
|
||||
let predicate = NSPredicate(format: "NONE \(#keyPath(TestEntity1.testToOne.testToManyUnordered)).\(#keyPath(TestEntity1.testString)) == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<TestEntity1>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).none() == dummy
|
||||
let predicate = NSPredicate(format: "NONE master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatWhereClauses_ConfigureCorrectly() {
|
||||
|
||||
do {
|
||||
|
||||
let whereClause = Where()
|
||||
XCTAssertEqual(whereClause, Where(true))
|
||||
XCTAssertNotEqual(whereClause, Where(false))
|
||||
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
|
||||
let whereClause = Where<NSManagedObject>()
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(true))
|
||||
XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
|
||||
XCTAssertAllEqual(whereClause.predicate, NSPredicate(value: true))
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause = Where(true)
|
||||
XCTAssertEqual(whereClause, Where())
|
||||
XCTAssertNotEqual(whereClause, Where(false))
|
||||
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
|
||||
let whereClause = Where<NSManagedObject>(true)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>())
|
||||
XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
|
||||
XCTAssertAllEqual(whereClause.predicate, NSPredicate(value: true))
|
||||
}
|
||||
do {
|
||||
|
||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||
let whereClause = Where(predicate)
|
||||
XCTAssertEqual(whereClause, Where(predicate))
|
||||
XCTAssertEqual(whereClause.predicate, predicate)
|
||||
let whereClause = Where<NSManagedObject>(predicate)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause = Where("%K == %@", "key", "value")
|
||||
let whereClause = Where<NSManagedObject>("%K == %@", "key", "value")
|
||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||
XCTAssertEqual(whereClause, Where(predicate))
|
||||
XCTAssertEqual(whereClause.predicate, predicate)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause = Where("%K == %@", argumentArray: ["key", "value"])
|
||||
let whereClause = Where<NSManagedObject>("%K == %@", argumentArray: ["key", "value"])
|
||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||
XCTAssertEqual(whereClause, Where(predicate))
|
||||
XCTAssertEqual(whereClause.predicate, predicate)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause = Where("key", isEqualTo: "value")
|
||||
let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
|
||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||
XCTAssertEqual(whereClause, Where(predicate))
|
||||
XCTAssertEqual(whereClause.predicate, predicate)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause = Where("key", isMemberOf: ["value1", "value2", "value3"])
|
||||
let whereClause = Where<NSManagedObject>("key", isMemberOf: ["value1", "value2", "value3"])
|
||||
let predicate = NSPredicate(format: "%K IN %@", "key", ["value1", "value2", "value3"])
|
||||
XCTAssertEqual(whereClause, Where(predicate))
|
||||
XCTAssertEqual(whereClause.predicate, predicate)
|
||||
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,211 +399,123 @@ final class WhereTests: XCTestCase {
|
||||
do {
|
||||
|
||||
let value: Int = 100
|
||||
let whereClause1 = Where("%K == %d", "key", value)
|
||||
let whereClause2 = Where("%K == %d", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %d", "key", NSNumber(value: value))
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause6 = Where("%K == %@", "key", NSNumber(value: value))
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %d", "key", value),
|
||||
Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %d", "key", NSNumber(value: value)),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", NSNumber(value: value)),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: NSNumber(value: value))
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value = NSNumber(value: 100)
|
||||
let whereClause1 = Where("%K == %d", "key", value)
|
||||
let whereClause2 = Where("%K == %d", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %d", "key", value.intValue)
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause6 = Where("%K == %@", "key", value.intValue)
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %d", "key", value),
|
||||
Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %d", "key", value.intValue),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", value.intValue),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: value.intValue)
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value: Int64 = Int64.max
|
||||
let whereClause1 = Where("%K == %d", "key", value)
|
||||
let whereClause2 = Where("%K == %d", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %d", "key", NSNumber(value: value))
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause6 = Where("%K == %@", "key", NSNumber(value: value))
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %d", "key", value),
|
||||
Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %d", "key", NSNumber(value: value)),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", NSNumber(value: value)),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: NSNumber(value: value))
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value = NSNumber(value: Int64.max)
|
||||
let whereClause1 = Where("%K == %d", "key", value)
|
||||
let whereClause2 = Where("%K == %d", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %d", "key", value.int64Value)
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause6 = Where("%K == %@", "key", value.int64Value)
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %d", "key", value),
|
||||
Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %d", "key", value.int64Value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", value.int64Value),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: value.int64Value)
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value: String = "value"
|
||||
let whereClause1 = Where("%K == %s", "key", value)
|
||||
let whereClause2 = Where("%K == %s", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %s", "key", NSString(string: value))
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause6 = Where("%K == %@", "key", NSString(string: value))
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %s", "key", value),
|
||||
Where<NSManagedObject>("%K == %s", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %s", "key", NSString(string: value)),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", NSString(string: value)),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: value as NSString),
|
||||
Where<NSManagedObject>("key", isEqualTo: NSString(string: value))
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value = NSString(string: "value")
|
||||
let whereClause1 = Where("%K == %s", "key", value)
|
||||
let whereClause2 = Where("%K == %s", "key", value as String)
|
||||
let whereClause3 = Where("%K == %s", "key", value as String as AnyObject)
|
||||
let whereClause4 = Where("%K == %@", "key", value)
|
||||
let whereClause5 = Where("%K == %@", "key", value as String)
|
||||
let whereClause6 = Where("%K == %@", "key", value as String as AnyObject)
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K == %s", "key", value),
|
||||
Where<NSManagedObject>("%K == %s", "key", value as String),
|
||||
Where<NSManagedObject>("%K == %s", "key", value as String as AnyObject),
|
||||
Where<NSManagedObject>("%K == %@", "key", value),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as String),
|
||||
Where<NSManagedObject>("%K == %@", "key", value as String as AnyObject),
|
||||
Where<NSManagedObject>("key", isEqualTo: value),
|
||||
Where<NSManagedObject>("key", isEqualTo: value as String),
|
||||
Where<NSManagedObject>("key", isEqualTo: value as String as NSString)
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value: [Int] = [100, 200]
|
||||
let whereClause1 = Where("%K == %@", "key", value)
|
||||
let whereClause2 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %@", "key", value as [AnyObject])
|
||||
let whereClause4 = Where("%K == %@", "key", value as NSArray)
|
||||
let whereClause5 = Where("%K == %@", "key", NSArray(array: value))
|
||||
let whereClause6 = Where("%K == %@", "key", value as AnyObject as! NSArray)
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K IN %@", "key", value),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as [AnyObject]),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as NSArray),
|
||||
Where<NSManagedObject>("%K IN %@", "key", NSArray(array: value)),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as AnyObject as! NSArray),
|
||||
Where<NSManagedObject>("key", isMemberOf: value)
|
||||
)
|
||||
}
|
||||
do {
|
||||
|
||||
let value: [Int64] = [Int64.min, 100, Int64.max]
|
||||
let whereClause1 = Where("%K == %@", "key", value)
|
||||
let whereClause2 = Where("%K == %@", "key", value as AnyObject)
|
||||
let whereClause3 = Where("%K == %@", "key", value as [AnyObject])
|
||||
let whereClause4 = Where("%K == %@", "key", value as NSArray)
|
||||
let whereClause5 = Where("%K == %@", "key", NSArray(array: value))
|
||||
let whereClause6 = Where("%K == %@", "key", value as AnyObject as! NSArray)
|
||||
XCTAssertEqual(whereClause1, whereClause2)
|
||||
XCTAssertEqual(whereClause1, whereClause3)
|
||||
XCTAssertEqual(whereClause1, whereClause4)
|
||||
XCTAssertEqual(whereClause1, whereClause5)
|
||||
XCTAssertEqual(whereClause1, whereClause6)
|
||||
XCTAssertEqual(whereClause2, whereClause3)
|
||||
XCTAssertEqual(whereClause2, whereClause4)
|
||||
XCTAssertEqual(whereClause2, whereClause5)
|
||||
XCTAssertEqual(whereClause2, whereClause6)
|
||||
XCTAssertEqual(whereClause3, whereClause4)
|
||||
XCTAssertEqual(whereClause3, whereClause5)
|
||||
XCTAssertEqual(whereClause3, whereClause6)
|
||||
XCTAssertEqual(whereClause4, whereClause5)
|
||||
XCTAssertEqual(whereClause4, whereClause6)
|
||||
XCTAssertEqual(whereClause5, whereClause6)
|
||||
XCTAssertAllEqual(
|
||||
Where<NSManagedObject>("%K IN %@", "key", value),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as AnyObject),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as [AnyObject]),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as NSArray),
|
||||
Where<NSManagedObject>("%K IN %@", "key", NSArray(array: value)),
|
||||
Where<NSManagedObject>("%K IN %@", "key", value as AnyObject as! NSArray),
|
||||
Where<NSManagedObject>("key", isMemberOf: value)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() {
|
||||
|
||||
let whereClause1 = Where("key1", isEqualTo: "value1")
|
||||
let whereClause2 = Where("key2", isEqualTo: "value2")
|
||||
let whereClause3 = Where("key3", isEqualTo: "value3")
|
||||
let whereClause1 = Where<NSManagedObject>("key1", isEqualTo: "value1")
|
||||
let whereClause2 = Where<NSManagedObject>("key2", isEqualTo: "value2")
|
||||
let whereClause3 = Where<NSManagedObject>("key3", isEqualTo: "value3")
|
||||
|
||||
do {
|
||||
|
||||
@@ -306,8 +524,8 @@ final class WhereTests: XCTestCase {
|
||||
type: .not,
|
||||
subpredicates: [whereClause1.predicate]
|
||||
)
|
||||
XCTAssertEqual(notWhere.predicate, notPredicate)
|
||||
XCTAssertEqual(notWhere, !whereClause1)
|
||||
XCTAssertAllEqual(notWhere.predicate, notPredicate)
|
||||
XCTAssertAllEqual(notWhere, !whereClause1)
|
||||
}
|
||||
do {
|
||||
|
||||
@@ -322,8 +540,23 @@ final class WhereTests: XCTestCase {
|
||||
whereClause3.predicate
|
||||
]
|
||||
)
|
||||
XCTAssertEqual(andWhere.predicate, andPredicate)
|
||||
XCTAssertEqual(andWhere, whereClause1 && whereClause2 && whereClause3)
|
||||
XCTAssertAllEqual(andWhere.predicate, andPredicate)
|
||||
XCTAssertAllEqual(andWhere, whereClause1 && whereClause2 && whereClause3)
|
||||
}
|
||||
do {
|
||||
|
||||
let andWhere = whereClause1 && whereClause2 && whereClause3
|
||||
let noneWhere: Where<NSManagedObject>? = nil
|
||||
let someWhere: Where<NSManagedObject>? = Where<NSManagedObject>("key4", isEqualTo: "value4")
|
||||
|
||||
|
||||
let finalNoneWhere = andWhere &&? noneWhere
|
||||
let finalSomeWhere = andWhere &&? someWhere
|
||||
let unwrappedFinalSomeWhere = andWhere && someWhere!
|
||||
|
||||
|
||||
XCTAssertAllEqual(andWhere.predicate, finalNoneWhere.predicate)
|
||||
XCTAssertAllEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
@@ -338,18 +571,33 @@ final class WhereTests: XCTestCase {
|
||||
whereClause3.predicate
|
||||
]
|
||||
)
|
||||
XCTAssertEqual(orWhere.predicate, orPredicate)
|
||||
XCTAssertEqual(orWhere, whereClause1 || whereClause2 || whereClause3)
|
||||
XCTAssertAllEqual(orWhere.predicate, orPredicate)
|
||||
XCTAssertAllEqual(orWhere, whereClause1 || whereClause2 || whereClause3)
|
||||
}
|
||||
do {
|
||||
|
||||
let orWhere = whereClause1 || whereClause2 || whereClause3
|
||||
let noneWhere: Where<NSManagedObject>? = nil
|
||||
let someWhere: Where<NSManagedObject>? = Where<NSManagedObject>("key4", isEqualTo: "value4")
|
||||
|
||||
|
||||
let finalNoneWhere = orWhere &&? noneWhere
|
||||
let finalSomeWhere = orWhere &&? someWhere
|
||||
let unwrappedFinalSomeWhere = orWhere && someWhere!
|
||||
|
||||
XCTAssertAllEqual(orWhere.predicate, finalNoneWhere.predicate)
|
||||
XCTAssertAllEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() {
|
||||
|
||||
let whereClause = Where("key", isEqualTo: "value")
|
||||
let request = CoreStoreFetchRequest()
|
||||
let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
|
||||
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||
whereClause.applyToFetchRequest(request)
|
||||
XCTAssertNotNil(request.predicate)
|
||||
XCTAssertEqual(request.predicate, whereClause.predicate)
|
||||
XCTAssertAllEqual(request.predicate, whereClause.predicate)
|
||||
}
|
||||
}
|
||||
|
||||
1038
Demo/Demo.xcodeproj/project.pbxproj
Normal file
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -14,10 +14,10 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B54AAD481AF4D26E00848AE0"
|
||||
BuildableName = "CoreStoreDemo.app"
|
||||
BlueprintName = "CoreStoreDemo"
|
||||
ReferencedContainer = "container:CoreStoreDemo.xcodeproj">
|
||||
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:Demo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -29,17 +29,6 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B54AAD481AF4D26E00848AE0"
|
||||
BuildableName = "CoreStoreDemo.app"
|
||||
BlueprintName = "CoreStoreDemo"
|
||||
ReferencedContainer = "container:CoreStoreDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -55,14 +44,12 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B54AAD481AF4D26E00848AE0"
|
||||
BuildableName = "CoreStoreDemo.app"
|
||||
BlueprintName = "CoreStoreDemo"
|
||||
ReferencedContainer = "container:CoreStoreDemo.xcodeproj">
|
||||
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:Demo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -74,10 +61,10 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B54AAD481AF4D26E00848AE0"
|
||||
BuildableName = "CoreStoreDemo.app"
|
||||
BlueprintName = "CoreStoreDemo"
|
||||
ReferencedContainer = "container:CoreStoreDemo.xcodeproj">
|
||||
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:Demo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
70
Demo/Info.plist
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarTintParameters</key>
|
||||
<dict>
|
||||
<key>UINavigationBar</key>
|
||||
<dict>
|
||||
<key>Style</key>
|
||||
<string>UIBarStyleDefault</string>
|
||||
<key>Translucent</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
42
Demo/Rakefile
Normal file
@@ -0,0 +1,42 @@
|
||||
# coding: utf-8
|
||||
|
||||
task :format do
|
||||
|
||||
require 'xcodeproj'
|
||||
require 'fileutils'
|
||||
|
||||
project_path = 'Demo.xcodeproj'
|
||||
ignore_targets = []
|
||||
|
||||
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj
|
||||
project = Xcodeproj::Project.open(project_path)
|
||||
validTargets = project.targets.select { |target| target.respond_to?(:product_type) }
|
||||
|
||||
validTargets.each do |target|
|
||||
if ignore_targets.include?(target.display_name)
|
||||
next
|
||||
end
|
||||
case target.product_type
|
||||
|
||||
when 'com.apple.product-type.application', 'com.apple.product-type.framework'
|
||||
target.source_build_phase.files.sort! do |f1, f2|
|
||||
result = (f1.display_name.count "+") <=> (f2.display_name.count "+")
|
||||
if result != 0
|
||||
next -result
|
||||
end
|
||||
result = (f1.display_name.count "-") <=> (f2.display_name.count "-")
|
||||
if result != 0
|
||||
next -result
|
||||
end
|
||||
result = (f1.display_name.count ".") <=> (f2.display_name.count ".")
|
||||
if result != 0
|
||||
next result
|
||||
end
|
||||
(f1.display_name <=> f2.display_name)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
project.save()
|
||||
end
|
||||
60
Demo/Resources/Base.lproj/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController interfaceStyle="dark" id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Bp2-lt-3DL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2020 John Rommel Estropia. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="Yn3-8H-uzI">
|
||||
<rect key="frame" x="20" y="827" width="374" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="CoreStoreIcon" translatesAutoresizingMaskIntoConstraints="NO" id="IrK-8p-pit">
|
||||
<rect key="frame" x="122" y="228.5" width="170" height="170"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="IrK-8p-pit" secondAttribute="height" multiplier="1:1" id="WaM-8F-33r"/>
|
||||
<constraint firstAttribute="width" constant="170" id="dlo-1N-ikz"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CoreStore" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="8Vu-0U-3hd">
|
||||
<rect key="frame" x="20" y="418.5" width="374" height="57.5"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="50"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Yn3-8H-uzI" firstAttribute="leading" secondItem="Bp2-lt-3DL" secondAttribute="leading" constant="20" symbolic="YES" id="7Dq-xP-k2v"/>
|
||||
<constraint firstItem="IrK-8p-pit" firstAttribute="centerY" secondItem="Bp2-lt-3DL" secondAttribute="centerY" multiplier="0.7" id="HUz-XL-l27"/>
|
||||
<constraint firstItem="IrK-8p-pit" firstAttribute="centerX" secondItem="Bp2-lt-3DL" secondAttribute="centerX" id="TSf-yM-hl1"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8Vu-0U-3hd" secondAttribute="centerX" id="TX2-HT-cKs"/>
|
||||
<constraint firstItem="Z3i-EZ-UGs" firstAttribute="bottom" secondItem="Yn3-8H-uzI" secondAttribute="bottom" constant="14" id="hAb-SJ-Qnm"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Yn3-8H-uzI" secondAttribute="centerX" id="pNf-eo-RXZ"/>
|
||||
<constraint firstItem="8Vu-0U-3hd" firstAttribute="leading" secondItem="Bp2-lt-3DL" secondAttribute="leading" constant="20" symbolic="YES" id="pef-yD-C5e"/>
|
||||
<constraint firstItem="8Vu-0U-3hd" firstAttribute="top" secondItem="IrK-8p-pit" secondAttribute="bottom" constant="20" id="xQP-tq-hNL"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Z3i-EZ-UGs"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="CoreStoreIcon" width="170" height="170"/>
|
||||
</resources>
|
||||
</document>
|
||||
115
Demo/Resources/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-60@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-60@3x-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-76@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-83.5@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "Mask + Oval 1 + Oval 1 + Oval 1.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"idiom" : "car",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-60@3x.png",
|
||||
"idiom" : "car",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 118 KiB |
6
Demo/Resources/Images.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
15
Demo/Resources/Images.xcassets/CoreStoreIcon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "CoreStoreIcon.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
BIN
Demo/Resources/Images.xcassets/CoreStoreIcon.imageset/CoreStoreIcon.pdf
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
33
Demo/⭐️Sources/AppDelegate.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
// MARK: - AppDelegate
|
||||
|
||||
@UIApplicationMain
|
||||
@objc final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
// MARK: UIApplicationDelegate
|
||||
|
||||
@objc dynamic func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc dynamic func application(
|
||||
_ application: UIApplication,
|
||||
configurationForConnecting connectingSceneSession: UISceneSession,
|
||||
options: UIScene.ConnectionOptions
|
||||
) -> UISceneConfiguration {
|
||||
|
||||
return UISceneConfiguration(
|
||||
name: "Default Configuration",
|
||||
sessionRole: connectingSceneSession.role
|
||||
)
|
||||
}
|
||||
}
|
||||
69
Demo/⭐️Sources/Helpers/ImageDownloader.swift
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
// MARK: - ImageDownloader
|
||||
|
||||
final class ImageDownloader: ObservableObject {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
private(set) var image: UIImage?
|
||||
|
||||
let url: URL?
|
||||
|
||||
init(url: URL?) {
|
||||
|
||||
self.url = url
|
||||
guard let url = url else {
|
||||
|
||||
return
|
||||
}
|
||||
if let image = Self.cache[url] {
|
||||
|
||||
self.image = image
|
||||
}
|
||||
}
|
||||
|
||||
func fetchImage(completion: @escaping (UIImage) -> Void = { _ in }) {
|
||||
|
||||
guard let url = url else {
|
||||
|
||||
return
|
||||
}
|
||||
if let image = Self.cache[url] {
|
||||
|
||||
self.objectWillChange.send()
|
||||
self.image = image
|
||||
completion(image)
|
||||
return
|
||||
}
|
||||
self.cancellable = URLSession.shared
|
||||
.dataTaskPublisher(for: url)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(
|
||||
receiveCompletion: { _ in },
|
||||
receiveValue: { output in
|
||||
|
||||
if let image = UIImage(data: output.data) {
|
||||
|
||||
Self.cache[url] = image
|
||||
self.objectWillChange.send()
|
||||
self.image = image
|
||||
completion(image)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private static var cache: [URL: UIImage] = [:]
|
||||
|
||||
private var cancellable: AnyCancellable?
|
||||
}
|
||||
58
Demo/⭐️Sources/Helpers/InstructionsView.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - InstructionsView
|
||||
|
||||
struct InstructionsView: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(_ rows: (header: String, description: String)...) {
|
||||
|
||||
self.rows = rows.map({ .init(header: $0, description: $1) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .center) {
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.fill(Color.white)
|
||||
.shadow(color: Color(.sRGB, white: 0.5, opacity: 0.3), radius: 2, x: 1, y: 1)
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
ForEach(self.rows, id: \.header) { row in
|
||||
HStack(alignment: .firstTextBaseline, spacing: 5) {
|
||||
Text(row.header)
|
||||
.font(.callout)
|
||||
.fontWeight(.bold)
|
||||
Text(row.description)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
.foregroundColor(Color(.sRGB, white: 0, opacity: 0.8))
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.fixedSize()
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let rows: [InstructionsView.Row]
|
||||
|
||||
|
||||
// MARK: - Row
|
||||
|
||||
struct Row: Hashable {
|
||||
|
||||
// MARK: Internal
|
||||
let header: String
|
||||
let description: String
|
||||
}
|
||||
}
|
||||
|
||||
29
Demo/⭐️Sources/Helpers/LazyView.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - LazyView
|
||||
|
||||
struct LazyView<Content: View>: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(_ load: @escaping () -> Content) {
|
||||
|
||||
self.load = load
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: Content {
|
||||
|
||||
self.load()
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let load: () -> Content
|
||||
}
|
||||
71
Demo/⭐️Sources/Helpers/Menu/Menu.ItemView.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
extension Menu {
|
||||
|
||||
// MARK: - Menu.ItemView
|
||||
|
||||
struct ItemView<Destination: View>: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(
|
||||
title: String,
|
||||
subtitle: String? = nil,
|
||||
destination: @escaping () -> Destination
|
||||
) {
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.destination = destination
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: LazyView(self.destination)) {
|
||||
VStack(alignment: .leading) {
|
||||
Text(self.title)
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
self.subtitle.map {
|
||||
Text($0)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate let title: String
|
||||
fileprivate let subtitle: String?
|
||||
fileprivate let destination: () -> Destination
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Menu_ItemView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
Menu.ItemView(
|
||||
title: "Item Title",
|
||||
subtitle: "A subtitle caption for this item",
|
||||
destination: {
|
||||
Color.blue
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
134
Demo/⭐️Sources/Helpers/Menu/Menu.MainView.swift
Normal file
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
extension Menu {
|
||||
|
||||
// MARK: - Menu.MainView
|
||||
|
||||
struct MainView: View {
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
Section(header: Text("Modern (CoreStoreObject subclasses)")) {
|
||||
Menu.ItemView(
|
||||
title: "Placemarks",
|
||||
subtitle: "Making changes using Transactions",
|
||||
destination: {
|
||||
Modern.PlacemarksDemo.MainView()
|
||||
}
|
||||
)
|
||||
Menu.ItemView(
|
||||
title: "Time Zones",
|
||||
subtitle: "Fetching objects and Querying raw values",
|
||||
destination: {
|
||||
Modern.TimeZonesDemo.MainView()
|
||||
}
|
||||
)
|
||||
Menu.ItemView(
|
||||
title: "Colors (UIKit)",
|
||||
subtitle: "Observing list changes and single-object changes using DiffableDataSources",
|
||||
destination: {
|
||||
Modern.ColorsDemo.MainView(
|
||||
listView: { listPublisher, onPaletteTapped in
|
||||
Modern.ColorsDemo.UIKit.ListView(
|
||||
listPublisher: listPublisher,
|
||||
onPaletteTapped: onPaletteTapped
|
||||
)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
},
|
||||
detailView: { objectPublisher in
|
||||
Modern.ColorsDemo.UIKit.DetailView(objectPublisher)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
Menu.ItemView(
|
||||
title: "Colors (SwiftUI)",
|
||||
subtitle: "Observing list changes and single-object changes using SwiftUI bindings",
|
||||
destination: {
|
||||
Modern.ColorsDemo.MainView(
|
||||
listView: { listPublisher, onPaletteTapped in
|
||||
Modern.ColorsDemo.SwiftUI.ListView(
|
||||
listPublisher: listPublisher,
|
||||
onPaletteTapped: onPaletteTapped
|
||||
)
|
||||
},
|
||||
detailView: { objectPublisher in
|
||||
Modern.ColorsDemo.SwiftUI.DetailView(objectPublisher)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
Menu.ItemView(
|
||||
title: "Pokedex API",
|
||||
subtitle: "Importing JSON data from external source",
|
||||
destination: {
|
||||
Modern.PokedexDemo.MainView(
|
||||
listView: Modern.PokedexDemo.UIKit.ListView.init
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
Section(header: Text("Classic (NSManagedObject subclasses)")) {
|
||||
Menu.ItemView(
|
||||
title: "Colors",
|
||||
subtitle: "Observing list changes and single-object changes using ListMonitor",
|
||||
destination: {
|
||||
Classic.ColorsDemo.MainView()
|
||||
}
|
||||
)
|
||||
}
|
||||
Section(header: Text("Advanced")) {
|
||||
Menu.ItemView(
|
||||
title: "Accounts",
|
||||
subtitle: "Switching between multiple persistent stores",
|
||||
destination: { EmptyView() }
|
||||
)
|
||||
.disabled(true)
|
||||
Menu.ItemView(
|
||||
title: "Evolution",
|
||||
subtitle: "Migrating and reverse-migrating stores",
|
||||
destination: {
|
||||
Advanced.EvolutionDemo.MainView()
|
||||
}
|
||||
)
|
||||
Menu.ItemView(
|
||||
title: "Logger",
|
||||
subtitle: "Implementing a custom logger",
|
||||
destination: { EmptyView() }
|
||||
)
|
||||
.disabled(true)
|
||||
}
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.navigationBarTitle("CoreStore Demos")
|
||||
Menu.PlaceholderView()
|
||||
}
|
||||
.navigationViewStyle(DoubleColumnNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Menu_MainView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
Menu.MainView()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
44
Demo/⭐️Sources/Helpers/Menu/Menu.PlaceholderView.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Combine
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
extension Menu {
|
||||
|
||||
// MARK: - Menu.PlaceholderView
|
||||
|
||||
struct PlaceholderView: UIViewControllerRepresentable {
|
||||
|
||||
// MARK: UIViewControllerRepresentable
|
||||
|
||||
typealias UIViewControllerType = UIViewController
|
||||
|
||||
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
|
||||
|
||||
return UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Self.Context) {}
|
||||
|
||||
static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator: Void) {}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Menu_PlaceholderView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
return Menu.PlaceholderView()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
10
Demo/⭐️Sources/Helpers/Menu/Menu.swift
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
enum Menu {}
|
||||
36
Demo/⭐️Sources/SceneDelegate.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// MARK: - SceneDelegate
|
||||
|
||||
@objc final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
// MARK: UIWindowSceneDelegate
|
||||
|
||||
@objc dynamic var window: UIWindow?
|
||||
|
||||
|
||||
// MARK: UISceneDelegate
|
||||
|
||||
@objc dynamic func scene(
|
||||
_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions
|
||||
) {
|
||||
|
||||
guard case let scene as UIWindowScene = scene else {
|
||||
|
||||
return
|
||||
}
|
||||
let window = UIWindow(windowScene: scene)
|
||||
window.rootViewController = UIHostingController(
|
||||
rootView: Menu.MainView()
|
||||
)
|
||||
self.window = window
|
||||
window.makeKeyAndVisible()
|
||||
}
|
||||
}
|
||||
10
Demo/⭐️Sources/⭐️Demos/⭐️Advanced/Advanced.swift
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
// MARK: - Advanced
|
||||
|
||||
/**
|
||||
Sample application of complex use cases
|
||||
*/
|
||||
enum Advanced {}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
typealias CreatureType = Advanced_EvolutionDemo_CreatureType
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.CreatureType
|
||||
|
||||
protocol Advanced_EvolutionDemo_CreatureType: DynamicObject, CustomStringConvertible {
|
||||
|
||||
var dnaCode: Int64 { get set }
|
||||
|
||||
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource
|
||||
|
||||
static func count(in transaction: BaseDataTransaction) throws -> Int
|
||||
|
||||
static func create(in transaction: BaseDataTransaction) -> Self
|
||||
|
||||
func mutate(in transaction: BaseDataTransaction)
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import Combine
|
||||
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.CreaturesDataSource
|
||||
|
||||
/**
|
||||
A type-erasing adapter to support different `ListPublisher` types
|
||||
*/
|
||||
final class CreaturesDataSource: ObservableObject {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init<T: NSManagedObject & Advanced.EvolutionDemo.CreatureType>(
|
||||
listPublisher: ListPublisher<T>,
|
||||
dataStack: DataStack
|
||||
) {
|
||||
|
||||
self.numberOfItems = {
|
||||
listPublisher.snapshot.numberOfItems
|
||||
}
|
||||
self.itemDescriptionAtIndex = { index in
|
||||
listPublisher.snapshot[index].object?.description
|
||||
}
|
||||
self.addItems = { count in
|
||||
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
let nextDNACode = try transaction.fetchCount(From<T>())
|
||||
for offset in 0 ..< count {
|
||||
|
||||
let object = transaction.create(Into<T>())
|
||||
object.dnaCode = .init(nextDNACode + offset)
|
||||
object.mutate(in: transaction)
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
self.mutateItemAtIndex = { index in
|
||||
|
||||
let object = listPublisher.snapshot[index]
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
object
|
||||
.asEditable(in: transaction)?
|
||||
.mutate(in: transaction)
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
self.deleteAllItems = {
|
||||
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
try transaction.deleteAll(From<T>())
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
|
||||
self?.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
init<T: CoreStoreObject & Advanced.EvolutionDemo.CreatureType>(
|
||||
listPublisher: ListPublisher<T>,
|
||||
dataStack: DataStack
|
||||
) {
|
||||
|
||||
self.numberOfItems = {
|
||||
listPublisher.snapshot.numberOfItems
|
||||
}
|
||||
self.itemDescriptionAtIndex = { index in
|
||||
listPublisher.snapshot[index].object?.description
|
||||
}
|
||||
self.addItems = { count in
|
||||
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
let nextDNACode = try transaction.fetchCount(From<T>())
|
||||
for offset in 0 ..< count {
|
||||
|
||||
let object = transaction.create(Into<T>())
|
||||
object.dnaCode = .init(nextDNACode + offset)
|
||||
object.mutate(in: transaction)
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
self.mutateItemAtIndex = { index in
|
||||
|
||||
let object = listPublisher.snapshot[index]
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
object
|
||||
.asEditable(in: transaction)?
|
||||
.mutate(in: transaction)
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
self.deleteAllItems = {
|
||||
|
||||
dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
try transaction.deleteAll(From<T>())
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
|
||||
self?.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfCreatures() -> Int {
|
||||
|
||||
return self.numberOfItems()
|
||||
}
|
||||
|
||||
func creatureDescription(at index: Int) -> String? {
|
||||
|
||||
return self.itemDescriptionAtIndex(index)
|
||||
}
|
||||
|
||||
func mutate(at index: Int) {
|
||||
|
||||
self.mutateItemAtIndex(index)
|
||||
}
|
||||
|
||||
func add(count: Int) {
|
||||
|
||||
self.addItems(count)
|
||||
}
|
||||
|
||||
func clear() {
|
||||
|
||||
self.deleteAllItems()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let numberOfItems: () -> Int
|
||||
private let itemDescriptionAtIndex: (Int) -> String?
|
||||
private let mutateItemAtIndex: (Int) -> Void
|
||||
private let addItems: (Int) -> Void
|
||||
private let deleteAllItems: () -> Void
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - AdvancedEvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - GeologicalPeriod
|
||||
|
||||
enum GeologicalPeriod: RawRepresentable, CaseIterable, Hashable, CustomStringConvertible {
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
case ageOfInvertebrates
|
||||
case ageOfFishes
|
||||
case ageOfReptiles
|
||||
case ageOfMammals
|
||||
|
||||
var version: ModelVersion {
|
||||
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var creatureType: Advanced.EvolutionDemo.CreatureType.Type {
|
||||
|
||||
switch self {
|
||||
|
||||
case .ageOfInvertebrates:
|
||||
return Advanced.EvolutionDemo.V1.Creature.self
|
||||
case .ageOfFishes:
|
||||
return Advanced.EvolutionDemo.V2.Creature.self
|
||||
case .ageOfReptiles:
|
||||
return Advanced.EvolutionDemo.V3.Creature.self
|
||||
case .ageOfMammals:
|
||||
return Advanced.EvolutionDemo.V4.Creature.self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
var description: String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .ageOfInvertebrates:
|
||||
return "Invertebrates"
|
||||
case .ageOfFishes:
|
||||
return "Fishes"
|
||||
case .ageOfReptiles:
|
||||
return "Reptiles"
|
||||
case .ageOfMammals:
|
||||
return "Mammals"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: RawRepresentable
|
||||
|
||||
typealias RawValue = ModelVersion
|
||||
|
||||
var rawValue: ModelVersion {
|
||||
|
||||
switch self {
|
||||
|
||||
case .ageOfInvertebrates:
|
||||
return Advanced.EvolutionDemo.V1.name
|
||||
case .ageOfFishes:
|
||||
return Advanced.EvolutionDemo.V2.name
|
||||
case .ageOfReptiles:
|
||||
return Advanced.EvolutionDemo.V3.name
|
||||
case .ageOfMammals:
|
||||
return Advanced.EvolutionDemo.V4.name
|
||||
}
|
||||
}
|
||||
|
||||
init?(rawValue: ModelVersion) {
|
||||
|
||||
switch rawValue {
|
||||
|
||||
case Advanced.EvolutionDemo.V1.name:
|
||||
self = .ageOfInvertebrates
|
||||
case Advanced.EvolutionDemo.V2.name:
|
||||
self = .ageOfFishes
|
||||
case Advanced.EvolutionDemo.V3.name:
|
||||
self = .ageOfReptiles
|
||||
case Advanced.EvolutionDemo.V4.name:
|
||||
self = .ageOfMammals
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.ItemView
|
||||
|
||||
struct ItemView: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(description: String?, mutate: @escaping () -> Void) {
|
||||
|
||||
self.description = description
|
||||
self.mutate = mutate
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(self.description ?? "")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Button(
|
||||
action: self.mutate,
|
||||
label: {
|
||||
Text("Mutate")
|
||||
.foregroundColor(.accentColor)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.disabled(self.description == nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate let description: String?
|
||||
fileprivate let mutate: () -> Void
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Advanced_EvolutionDemo_ItemView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
Advanced.EvolutionDemo.ItemView(
|
||||
description: """
|
||||
dnaCode: 123
|
||||
numberOfLimbs: 4
|
||||
hasVertebrae: true
|
||||
hasHead: true
|
||||
hasTail: true
|
||||
habitat: land
|
||||
hasWings: false
|
||||
""",
|
||||
mutate: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.ListView
|
||||
|
||||
struct ListView: View {
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
let dataSource = self.dataSource
|
||||
return List {
|
||||
ForEach(0 ..< dataSource.numberOfCreatures(), id: \.self) { (index) in
|
||||
Advanced.EvolutionDemo.ItemView(
|
||||
description: dataSource.creatureDescription(at: index),
|
||||
mutate: {
|
||||
|
||||
dataSource.mutate(at: index)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
.listStyle(PlainListStyle())
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(
|
||||
period: Advanced.EvolutionDemo.GeologicalPeriod,
|
||||
dataStack: DataStack,
|
||||
dataSource: Advanced.EvolutionDemo.CreaturesDataSource
|
||||
) {
|
||||
|
||||
self.period = period
|
||||
self.dataStack = dataStack
|
||||
self.dataSource = dataSource
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let period: Advanced.EvolutionDemo.GeologicalPeriod
|
||||
|
||||
private let dataStack: DataStack
|
||||
|
||||
@ObservedObject
|
||||
private var dataSource: Advanced.EvolutionDemo.CreaturesDataSource
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Advanced_EvolutionDemo_ListView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
let dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: Advanced.EvolutionDemo.V4.name,
|
||||
entities: [
|
||||
Entity<Advanced.EvolutionDemo.V4.Creature>("Creature")
|
||||
]
|
||||
)
|
||||
)
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(fileName: "Advanced.EvolutionDemo.ListView.Preview.sqlite")
|
||||
)
|
||||
try! dataStack.perform(
|
||||
synchronous: { transaction in
|
||||
|
||||
for dnaCode in 0 ..< 10 as Range<Int64> {
|
||||
|
||||
let object = transaction.create(Into<Advanced.EvolutionDemo.V4.Creature>())
|
||||
object.dnaCode = dnaCode
|
||||
object.mutate(in: transaction)
|
||||
}
|
||||
}
|
||||
)
|
||||
return Advanced.EvolutionDemo.ListView(
|
||||
period: .ageOfMammals,
|
||||
dataStack: dataStack,
|
||||
dataSource: Advanced.EvolutionDemo.V4.Creature.dataSource(in: dataStack)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.MainView
|
||||
|
||||
struct MainView: View {
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
let migrator = self.migrator
|
||||
let listView: AnyView
|
||||
if let current = migrator.current {
|
||||
|
||||
listView = AnyView(
|
||||
Advanced.EvolutionDemo.ListView(
|
||||
period: current.period,
|
||||
dataStack: current.dataStack,
|
||||
dataSource: current.dataSource
|
||||
)
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
||||
listView = AnyView(
|
||||
Advanced.EvolutionDemo.ProgressView(progress: migrator.progress)
|
||||
)
|
||||
}
|
||||
|
||||
return VStack(spacing: 0) {
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
Text("Age of")
|
||||
.padding(.trailing)
|
||||
Picker(selection: self.$migrator.currentPeriod, label: EmptyView()) {
|
||||
ForEach(Advanced.EvolutionDemo.GeologicalPeriod.allCases, id: \.self) { period in
|
||||
Text(period.description).tag(period)
|
||||
}
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
}
|
||||
.padding()
|
||||
listView
|
||||
.edgesIgnoringSafeArea(.vertical)
|
||||
}
|
||||
.navigationBarTitle("Evolution")
|
||||
.disabled(migrator.isBusy || migrator.current == nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@ObservedObject
|
||||
private var migrator: Advanced.EvolutionDemo.Migrator = .init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Advanced_EvolutionDemo_MainView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
Advanced.EvolutionDemo.MainView()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo
|
||||
|
||||
extension Advanced.EvolutionDemo {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.ProgressView
|
||||
|
||||
struct ProgressView: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(progress: Progress?) {
|
||||
|
||||
self.progressObserver = .init(progress)
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
|
||||
guard self.progressObserver.isMigrating else {
|
||||
|
||||
return AnyView(
|
||||
VStack(alignment: .center) {
|
||||
Text("Preparing creatures...")
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
)
|
||||
}
|
||||
return AnyView(
|
||||
VStack(alignment: .leading) {
|
||||
Text("Migrating: \(self.progressObserver.localizedDescription)")
|
||||
.font(.headline)
|
||||
.padding([.top, .horizontal])
|
||||
Text("Progressive step: \(self.progressObserver.localizedAdditionalDescription)")
|
||||
.font(.subheadline)
|
||||
.padding(.horizontal)
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .leading) {
|
||||
RoundedRectangle(cornerRadius: 4, style: .continuous)
|
||||
.fill(Color.gray.opacity(0.2))
|
||||
.frame(width: geometry.size.width, height: 8)
|
||||
RoundedRectangle(cornerRadius: 4, style: .continuous)
|
||||
.fill(Color.blue)
|
||||
.frame(
|
||||
width: geometry.size.width
|
||||
* self.progressObserver.fractionCompleted,
|
||||
height: 8
|
||||
)
|
||||
}
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
@ObservedObject
|
||||
private var progressObserver: ProgressObserver
|
||||
|
||||
|
||||
// MARK: - ProgressObserver
|
||||
|
||||
fileprivate final class ProgressObserver: ObservableObject {
|
||||
|
||||
private(set) var fractionCompleted: CGFloat = 0
|
||||
private(set) var localizedDescription: String = ""
|
||||
private(set) var localizedAdditionalDescription: String = ""
|
||||
|
||||
var isMigrating: Bool {
|
||||
|
||||
return self.progress != nil
|
||||
}
|
||||
|
||||
init(_ progress: Progress?) {
|
||||
|
||||
self.progress = progress
|
||||
|
||||
progress?.setProgressHandler { [weak self] (progess) in
|
||||
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.objectWillChange.send()
|
||||
self.fractionCompleted = CGFloat(progress?.fractionCompleted ?? 0)
|
||||
self.localizedDescription = progress?.localizedDescription ?? ""
|
||||
self.localizedAdditionalDescription = progress?.localizedAdditionalDescription ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let progress: Progress?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Advanced_EvolutionDemo_ProgressView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
let progress = Progress(totalUnitCount: 10)
|
||||
progress.completedUnitCount = 3
|
||||
return Advanced.EvolutionDemo.ProgressView(
|
||||
progress: progress
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.V1.Creature
|
||||
|
||||
@objc(Advanced_EvolutionDemo_V1_Creature)
|
||||
final class Advanced_EvolutionDemo_V1_Creature: NSManagedObject, Advanced.EvolutionDemo.CreatureType {
|
||||
|
||||
@NSManaged
|
||||
dynamic var dnaCode: Int64
|
||||
|
||||
@NSManaged
|
||||
dynamic var numberOfFlagella: Int32
|
||||
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
override var description: String {
|
||||
|
||||
return """
|
||||
dnaCode: \(self.dnaCode)
|
||||
numberOfFlagella: \(self.numberOfFlagella)
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
// MARK: Advanced.EvolutionDemo.CreatureType
|
||||
|
||||
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource {
|
||||
|
||||
return .init(
|
||||
listPublisher: dataStack.publishList(
|
||||
From<Advanced.EvolutionDemo.V1.Creature>()
|
||||
.orderBy(.descending(\.dnaCode))
|
||||
),
|
||||
dataStack: dataStack
|
||||
)
|
||||
}
|
||||
|
||||
static func count(in transaction: BaseDataTransaction) throws -> Int {
|
||||
|
||||
return try transaction.fetchCount(
|
||||
From<Advanced.EvolutionDemo.V1.Creature>()
|
||||
)
|
||||
}
|
||||
|
||||
static func create(in transaction: BaseDataTransaction) -> Advanced.EvolutionDemo.V1.Creature {
|
||||
|
||||
return transaction.create(
|
||||
Into<Advanced.EvolutionDemo.V1.Creature>()
|
||||
)
|
||||
}
|
||||
|
||||
func mutate(in transaction: BaseDataTransaction) {
|
||||
|
||||
self.numberOfFlagella = .random(in: 1...200)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.V1
|
||||
|
||||
extension Advanced.EvolutionDemo.V1 {
|
||||
|
||||
// MARK: - Advanced.EvolutionDemo.V1.FromV2
|
||||
|
||||
enum FromV2 {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
static var mapping: XcodeSchemaMappingProvider {
|
||||
|
||||
return XcodeSchemaMappingProvider(
|
||||
from: Advanced.EvolutionDemo.V2.name,
|
||||
to: Advanced.EvolutionDemo.V1.name,
|
||||
mappingModelBundle: Bundle(for: Advanced.EvolutionDemo.V1.Creature.self)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||