Compare commits
673 Commits
2.0.5
...
transactio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf10f4668c | ||
|
|
a1a04aaf8a | ||
|
|
4ddfa95140 | ||
|
|
45215c7a18 | ||
|
|
d2f1656fdd | ||
|
|
5fbb1ab09d | ||
|
|
d0cc01877e | ||
|
|
a434628249 | ||
|
|
9cc616f720 | ||
|
|
4534bc06f5 | ||
|
|
798d30bbd9 | ||
|
|
7938aa2447 | ||
|
|
dda69389eb | ||
|
|
b20be10a19 | ||
|
|
0ee8fbabd5 | ||
|
|
1f562b25a7 | ||
|
|
6a2394052c | ||
|
|
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 | ||
|
|
19fea6953a | ||
|
|
9f11b1005d | ||
|
|
698326f89a | ||
|
|
bdf6308d8f | ||
|
|
69d96c53d6 | ||
|
|
7b961fa249 | ||
|
|
73450d0b29 | ||
|
|
6d83564a1a | ||
|
|
c0d72799b4 | ||
|
|
06a1919e91 | ||
|
|
16def2d84b | ||
|
|
e9a2c58f32 | ||
|
|
1a3e0dd4c6 | ||
|
|
518bb134f9 | ||
|
|
4d7feca848 | ||
|
|
64a80bf401 | ||
|
|
4d63fc744a | ||
|
|
9480e372f1 | ||
|
|
23df460c35 | ||
|
|
1c233b7302 | ||
|
|
f42288802c | ||
|
|
a27556f294 | ||
|
|
e330291e1b | ||
|
|
4a282150f0 | ||
|
|
5d2956d674 | ||
|
|
92756fec42 | ||
|
|
2b37daefe0 | ||
|
|
5724d4599e | ||
|
|
4a882e6108 | ||
|
|
b230ed6400 | ||
|
|
33a5c123aa | ||
|
|
73637321ce | ||
|
|
088f1717f9 | ||
|
|
cff2bb1740 | ||
|
|
d902d62172 | ||
|
|
b955495012 | ||
|
|
d2e78a70e1 | ||
|
|
970957cbc2 | ||
|
|
2f9e5db89f | ||
|
|
1789eb7daf | ||
|
|
3999654ee7 | ||
|
|
4c3bec287c | ||
|
|
2a2d9b3483 | ||
|
|
32a388e0ca | ||
|
|
1a6fbad3d4 | ||
|
|
ac55f20f5f | ||
|
|
910b5039fd | ||
|
|
ffea06ee7e | ||
|
|
a92d6cac02 | ||
|
|
de5d660257 | ||
|
|
55b2e6eecd | ||
|
|
b64c776335 | ||
|
|
432af667e8 | ||
|
|
cf60a4bc2e | ||
|
|
c620859899 | ||
|
|
65ac069a0b | ||
|
|
862ef27374 | ||
|
|
243b6a76d5 | ||
|
|
8be20370d5 | ||
|
|
a9c0feae46 | ||
|
|
2e44f86eb6 | ||
|
|
ed8c7b35e8 | ||
|
|
4d2ebe4ea8 | ||
|
|
54be9d471c | ||
|
|
f18d62f643 | ||
|
|
af141d4a31 | ||
|
|
2da659a967 | ||
|
|
e5f162c5e1 | ||
|
|
effa231719 | ||
|
|
6cef8f4b4f | ||
|
|
aa6bceaaf3 | ||
|
|
0dbd05b172 | ||
|
|
243c4044ab | ||
|
|
df835114cb | ||
|
|
f99d3cc21a | ||
|
|
a51ed1a007 | ||
|
|
e5245a0e5b | ||
|
|
0fa2a23461 | ||
|
|
3f28198552 | ||
|
|
82de482191 | ||
|
|
b502895d63 | ||
|
|
0ba63c6e72 | ||
|
|
e9be711d4c | ||
|
|
db5b8ca702 | ||
|
|
2f39f9188b | ||
|
|
872e69ddc6 | ||
|
|
f9e33101a0 | ||
|
|
0621c54868 | ||
|
|
a638620858 | ||
|
|
267c21063a |
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
|
.DS_Store
|
||||||
DerivedData
|
DerivedData
|
||||||
*.orig
|
*.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
|
||||||
|
|||||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "Carthage/Checkouts/GCDKit"]
|
|
||||||
path = Carthage/Checkouts/GCDKit
|
|
||||||
url = https://github.com/JohnEstropia/GCDKit.git
|
|
||||||
|
|||||||
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
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "self:CoreStoreDemo.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
@@ -2,9 +2,7 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
<true/>
|
|
||||||
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
|
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</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>
|
||||||
43
.travis.yml
@@ -1,43 +0,0 @@
|
|||||||
language: objective-c
|
|
||||||
osx_image: xcode7.3
|
|
||||||
sudo: false
|
|
||||||
git:
|
|
||||||
submodules: false
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- LC_CTYPE=en_US.UTF-8
|
|
||||||
- LANG=en_US.UTF-8
|
|
||||||
matrix:
|
|
||||||
- DESTINATION="OS=9.3,name=iPhone 6s" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=8.2,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="YES"
|
|
||||||
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="CoreStore iOS7" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="YES"
|
|
||||||
- DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.11 RUN_TESTS="YES" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator2.2 RUN_TESTS="NO" POD_LINT="NO"
|
|
||||||
- DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator9.2 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.11/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 "iphonesimulator9.3" -destination "OS=9.3,name=iPhone 6s" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
|
||||||
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator9.3" -destination "OS=9.3,name=iPhone 6s" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
|
|
||||||
- if [ $POD_LINT == "YES" ]; then
|
|
||||||
pod lib lint --quick;
|
|
||||||
fi
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
github "JohnEstropia/GCDKit" "1.2.6"
|
|
||||||
1
Carthage/Checkouts/GCDKit
vendored
BIN
CoreStore.png
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 61 KiB |
@@ -1,24 +1,22 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "CoreStore"
|
s.name = "CoreStore"
|
||||||
s.version = "2.0.5"
|
s.version = "8.0.1"
|
||||||
|
s.swift_version = "5.4"
|
||||||
s.license = "MIT"
|
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.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.author = { "John Rommel Estropia" => "rommel.estropia@gmail.com" }
|
||||||
s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s }
|
s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s }
|
||||||
|
|
||||||
s.ios.deployment_target = "8.0"
|
s.ios.deployment_target = "11.0"
|
||||||
s.osx.deployment_target = "10.10"
|
s.osx.deployment_target = "10.13"
|
||||||
s.watchos.deployment_target = "2.0"
|
s.watchos.deployment_target = "4.0"
|
||||||
s.tvos.deployment_target = "9.0"
|
s.tvos.deployment_target = "11.0"
|
||||||
|
|
||||||
s.source_files = "Sources", "Sources/**/*.{swift,h,m}"
|
s.source_files = "Sources", "Sources/**/*.{swift,h,m}"
|
||||||
s.public_header_files = "Sources/**/*.h"
|
s.public_header_files = "Sources/**/*.h"
|
||||||
s.frameworks = "Foundation", "CoreData"
|
s.frameworks = "Foundation", "CoreData"
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D USE_FRAMEWORKS -D DEBUG',
|
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D DEBUG', 'OTHER_LDFLAGS' => '-weak_framework Combine -weak_framework SwiftUI' }
|
||||||
'OTHER_SWIFT_FLAGS[config=Release]' => '-D USE_FRAMEWORKS',
|
|
||||||
'GCC_PREPROCESSOR_DEFINITIONS' => 'USE_FRAMEWORKS=1' }
|
|
||||||
|
|
||||||
s.dependency "GCDKit", "1.2.6"
|
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
CoreStore.sketch
@@ -10,10 +10,10 @@
|
|||||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD",
|
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD",
|
||||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
||||||
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit",
|
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit",
|
||||||
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore"
|
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore\/"
|
||||||
},
|
},
|
||||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore",
|
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore",
|
||||||
"DVTSourceControlWorkspaceBlueprintVersion" : 203,
|
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
|
||||||
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj",
|
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj",
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0710"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,6 +27,15 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
|
||||||
|
BuildableName = "CoreStore.framework"
|
||||||
|
BlueprintName = "CoreStore OSX"
|
||||||
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
@@ -39,17 +48,6 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
|
|
||||||
BuildableName = "CoreStore.framework"
|
|
||||||
BlueprintName = "CoreStore OSX"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -70,8 +68,6 @@
|
|||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0700"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -11,8 +11,7 @@
|
|||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
buildForProfiling = "YES"
|
buildForProfiling = "YES"
|
||||||
buildForArchiving = "YES"
|
buildForArchiving = "YES"
|
||||||
buildForAnalyzing = "YES"
|
buildForAnalyzing = "YES">
|
||||||
hideIssues = "NO">
|
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
|
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
|
||||||
@@ -26,8 +25,7 @@
|
|||||||
buildForRunning = "NO"
|
buildForRunning = "NO"
|
||||||
buildForProfiling = "NO"
|
buildForProfiling = "NO"
|
||||||
buildForArchiving = "NO"
|
buildForArchiving = "NO"
|
||||||
buildForAnalyzing = "NO"
|
buildForAnalyzing = "NO">
|
||||||
hideIssues = "NO">
|
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
|
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
|
||||||
@@ -43,6 +41,22 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
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>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
@@ -55,17 +69,6 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
|
|
||||||
BuildableName = "CoreStore.framework"
|
|
||||||
BlueprintName = "CoreStore iOS"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -86,8 +89,12 @@
|
|||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<CommandLineArguments>
|
||||||
</AdditionalOptions>
|
<CommandLineArgument
|
||||||
|
argument = "-com.apple.CoreData.SQLDebug 2"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "0700"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
|
|
||||||
BuildableName = "CoreStore_iOS7.framework"
|
|
||||||
BlueprintName = "CoreStore iOS7"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "NO"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
|
|
||||||
BuildableName = "CoreStoreTests.xctest"
|
|
||||||
BlueprintName = "CoreStoreTests iOS"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
|
|
||||||
BuildableName = "CoreStoreTests.xctest"
|
|
||||||
BlueprintName = "CoreStoreTests iOS"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</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"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
|
|
||||||
BuildableName = "CoreStore_iOS7.framework"
|
|
||||||
BlueprintName = "CoreStore iOS7"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Release">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0720"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,6 +27,15 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
|
||||||
|
BuildableName = "CoreStore.framework"
|
||||||
|
BlueprintName = "CoreStore tvOS"
|
||||||
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
@@ -39,17 +48,6 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
|
|
||||||
BuildableName = "CoreStore.framework"
|
|
||||||
BlueprintName = "CoreStore tvOS"
|
|
||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -70,8 +68,6 @@
|
|||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0700"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -29,8 +29,6 @@
|
|||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -51,8 +49,6 @@
|
|||||||
ReferencedContainer = "container:CoreStore.xcodeproj">
|
ReferencedContainer = "container:CoreStore.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
8
CoreStore.xcworkspace/contents.xcworkspacedata
generated
@@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Demo/Demo.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:CoreStore.xcodeproj">
|
location = "group:CoreStore.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:CoreStoreDemo/CoreStoreDemo.xcodeproj">
|
location = "group:LegacyDemo/LegacyDemo.xcodeproj">
|
||||||
</FileRef>
|
|
||||||
<FileRef
|
|
||||||
location = "group:Carthage/Checkouts/GCDKit/GCDKit.xcodeproj">
|
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</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,293 +0,0 @@
|
|||||||
//
|
|
||||||
// FetchingAndQueryingDemoViewController.swift
|
|
||||||
// CoreStoreDemo
|
|
||||||
//
|
|
||||||
// Created by John Rommel Estropia on 2015/06/12.
|
|
||||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import CoreStore
|
|
||||||
|
|
||||||
|
|
||||||
private struct Static {
|
|
||||||
|
|
||||||
static let timeZonesStack: DataStack = {
|
|
||||||
|
|
||||||
let dataStack = DataStack()
|
|
||||||
try! dataStack.addStorageAndWait(
|
|
||||||
SQLiteStore(
|
|
||||||
fileName: "TimeZoneDemo.sqlite",
|
|
||||||
configuration: "FetchingAndQueryingDemo",
|
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
dataStack.beginSynchronous { (transaction) -> Void in
|
|
||||||
|
|
||||||
transaction.deleteAll(From(TimeZone))
|
|
||||||
|
|
||||||
for name in NSTimeZone.knownTimeZoneNames() {
|
|
||||||
|
|
||||||
let rawTimeZone = NSTimeZone(name: name)!
|
|
||||||
let cachedTimeZone = transaction.create(Into(TimeZone))
|
|
||||||
|
|
||||||
cachedTimeZone.name = rawTimeZone.name
|
|
||||||
cachedTimeZone.abbreviation = rawTimeZone.abbreviation ?? ""
|
|
||||||
cachedTimeZone.secondsFromGMT = Int32(rawTimeZone.secondsFromGMT)
|
|
||||||
cachedTimeZone.hasDaylightSavingTime = rawTimeZone.daylightSavingTime
|
|
||||||
cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.commitAndWait()
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataStack
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - FetchingAndQueryingDemoViewController
|
|
||||||
|
|
||||||
class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
|
||||||
|
|
||||||
// MARK: UIViewController
|
|
||||||
|
|
||||||
override func viewDidAppear(animated: Bool) {
|
|
||||||
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
if self.didAppearOnce {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.didAppearOnce = true
|
|
||||||
|
|
||||||
let alert = UIAlertController(
|
|
||||||
title: "Fetch and Query Demo",
|
|
||||||
message: "This demo shows how to execute fetches and queries.\n\nEach menu item executes and displays a preconfigured fetch/query.",
|
|
||||||
preferredStyle: .Alert
|
|
||||||
)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
|
|
||||||
self.presentViewController(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
|
|
||||||
|
|
||||||
super.prepareForSegue(segue, sender: sender)
|
|
||||||
|
|
||||||
if let indexPath = sender as? NSIndexPath {
|
|
||||||
|
|
||||||
switch segue.destinationViewController {
|
|
||||||
|
|
||||||
case let controller as FetchingResultsViewController:
|
|
||||||
let item = self.fetchingItems[indexPath.row]
|
|
||||||
controller.setTimeZones(item.fetch(), title: item.title)
|
|
||||||
|
|
||||||
case let controller as QueryingResultsViewController:
|
|
||||||
let item = self.queryingItems[indexPath.row]
|
|
||||||
controller.setValue(item.query(), title: item.title)
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDataSource
|
|
||||||
|
|
||||||
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
|
|
||||||
switch self.segmentedControl?.selectedSegmentIndex {
|
|
||||||
|
|
||||||
case Section.Fetching.rawValue?:
|
|
||||||
return self.fetchingItems.count
|
|
||||||
|
|
||||||
case Section.Querying.rawValue?:
|
|
||||||
return self.queryingItems.count
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell")!
|
|
||||||
|
|
||||||
switch self.segmentedControl?.selectedSegmentIndex {
|
|
||||||
|
|
||||||
case Section.Fetching.rawValue?:
|
|
||||||
cell.textLabel?.text = self.fetchingItems[indexPath.row].title
|
|
||||||
|
|
||||||
case Section.Querying.rawValue?:
|
|
||||||
cell.textLabel?.text = self.queryingItems[indexPath.row].title
|
|
||||||
|
|
||||||
default:
|
|
||||||
cell.textLabel?.text = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDelegate
|
|
||||||
|
|
||||||
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
tableView.deselectRowAtIndexPath(indexPath, animated: true)
|
|
||||||
|
|
||||||
switch self.segmentedControl?.selectedSegmentIndex {
|
|
||||||
|
|
||||||
case Section.Fetching.rawValue?:
|
|
||||||
self.performSegueWithIdentifier("FetchingResultsViewController", sender: indexPath)
|
|
||||||
|
|
||||||
case Section.Querying.rawValue?:
|
|
||||||
self.performSegueWithIdentifier("QueryingResultsViewController", sender: indexPath)
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private enum Section: Int {
|
|
||||||
|
|
||||||
case Fetching
|
|
||||||
case Querying
|
|
||||||
}
|
|
||||||
|
|
||||||
private let fetchingItems = [
|
|
||||||
(
|
|
||||||
title: "All Time Zones",
|
|
||||||
fetch: { () -> [TimeZone] in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.fetchAll(
|
|
||||||
From(TimeZone),
|
|
||||||
OrderBy(.Ascending("name"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Time Zones in Asia",
|
|
||||||
fetch: { () -> [TimeZone] in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.fetchAll(
|
|
||||||
From(TimeZone),
|
|
||||||
Where("%K BEGINSWITH[c] %@", "name", "Asia"),
|
|
||||||
OrderBy(.Ascending("secondsFromGMT"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Time Zones in America and Europe",
|
|
||||||
fetch: { () -> [TimeZone] in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.fetchAll(
|
|
||||||
From(TimeZone),
|
|
||||||
Where("%K BEGINSWITH[c] %@", "name", "America")
|
|
||||||
|| Where("%K BEGINSWITH[c] %@", "name", "Europe"),
|
|
||||||
OrderBy(.Ascending("secondsFromGMT"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "All Time Zones Except America",
|
|
||||||
fetch: { () -> [TimeZone] in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.fetchAll(
|
|
||||||
From(TimeZone),
|
|
||||||
!Where("%K BEGINSWITH[c] %@", "name", "America"),
|
|
||||||
OrderBy(.Ascending("secondsFromGMT"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Time Zones with Summer Time",
|
|
||||||
fetch: { () -> [TimeZone] in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.fetchAll(
|
|
||||||
From(TimeZone),
|
|
||||||
Where("hasDaylightSavingTime", isEqualTo: true),
|
|
||||||
OrderBy(.Ascending("name"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
private let queryingItems = [
|
|
||||||
(
|
|
||||||
title: "Number of Time Zones",
|
|
||||||
query: { () -> AnyObject in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.queryValue(
|
|
||||||
From(TimeZone),
|
|
||||||
Select<NSNumber>(.Count("name"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Abbreviation For Tokyo's Time Zone",
|
|
||||||
query: { () -> AnyObject in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.queryValue(
|
|
||||||
From(TimeZone),
|
|
||||||
Select<String>("abbreviation"),
|
|
||||||
Where("%K ENDSWITH[c] %@", "name", "Tokyo")
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "All Abbreviations",
|
|
||||||
query: { () -> AnyObject in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.queryAttributes(
|
|
||||||
From(TimeZone),
|
|
||||||
Select<NSDictionary>("name", "abbreviation"),
|
|
||||||
OrderBy(.Ascending("name"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Number of Countries per Time Zone",
|
|
||||||
query: { () -> AnyObject in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.queryAttributes(
|
|
||||||
From(TimeZone),
|
|
||||||
Select<NSDictionary>(.Count("abbreviation"), "abbreviation"),
|
|
||||||
GroupBy("abbreviation"),
|
|
||||||
OrderBy(.Ascending("secondsFromGMT"), .Ascending("name"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(
|
|
||||||
title: "Number of Countries with Summer Time",
|
|
||||||
query: { () -> AnyObject in
|
|
||||||
|
|
||||||
return Static.timeZonesStack.queryAttributes(
|
|
||||||
From(TimeZone),
|
|
||||||
Select<NSDictionary>(
|
|
||||||
.Count("hasDaylightSavingTime", As: "numberOfCountries"),
|
|
||||||
"hasDaylightSavingTime"
|
|
||||||
),
|
|
||||||
GroupBy("hasDaylightSavingTime"),
|
|
||||||
OrderBy(.Descending("hasDaylightSavingTime"))
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
var didAppearOnce = false
|
|
||||||
|
|
||||||
@IBOutlet dynamic weak var segmentedControl: UISegmentedControl?
|
|
||||||
@IBOutlet dynamic weak var tableView: UITableView?
|
|
||||||
|
|
||||||
@IBAction dynamic func segmentedControlValueChanged(sender: AnyObject?) {
|
|
||||||
|
|
||||||
self.tableView?.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,306 +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("brightness >= 0.9")
|
|
||||||
case .Dark: return Where("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("colorName"),
|
|
||||||
OrderBy(.Ascending("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.setTableEnabled(!Static.palettes.isPendingRefetch)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
|
|
||||||
|
|
||||||
super.prepareForSegue(segue, sender: sender)
|
|
||||||
|
|
||||||
switch (segue.identifier, segue.destinationViewController, sender) {
|
|
||||||
|
|
||||||
case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as Palette):
|
|
||||||
destinationViewController.palette = palette
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDataSource
|
|
||||||
|
|
||||||
override func numberOfSectionsInTableView(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, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCellWithIdentifier("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, didSelectRowAtIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
tableView.deselectRowAtIndexPath(indexPath, animated: true)
|
|
||||||
|
|
||||||
self.performSegueWithIdentifier(
|
|
||||||
"ObjectObserverDemoViewController",
|
|
||||||
sender: Static.palettes[indexPath]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
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.setTableEnabled(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitorDidRefetch(monitor: ListMonitor<Palette>) {
|
|
||||||
|
|
||||||
self.filterBarButton?.title = Static.filter.rawValue
|
|
||||||
self.tableView.reloadData()
|
|
||||||
self.setTableEnabled(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ListObjectObserver
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<Palette>, didInsertObject object: Palette, toIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<Palette>, didDeleteObject object: Palette, fromIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<Palette>, didUpdateObject object: Palette, atIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
if let cell = self.tableView.cellForRowAtIndexPath(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: NSIndexPath, toIndexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
self.tableView.deleteRowsAtIndexPaths([fromIndexPath], withRowAnimation: .Automatic)
|
|
||||||
self.tableView.insertRowsAtIndexPaths([toIndexPath], withRowAnimation: .Automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ListSectionObserver
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<Palette>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
|
||||||
|
|
||||||
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<Palette>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
|
||||||
|
|
||||||
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .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 setTableEnabled(enabled: Bool) {
|
|
||||||
|
|
||||||
UIView.animateWithDuration(
|
|
||||||
0.2,
|
|
||||||
delay: 0,
|
|
||||||
options: .BeginFromCurrentState,
|
|
||||||
animations: { () -> Void in
|
|
||||||
|
|
||||||
if let tableView = self.tableView {
|
|
||||||
|
|
||||||
tableView.alpha = enabled ? 1.0 : 0.5
|
|
||||||
tableView.userInteractionEnabled = enabled
|
|
||||||
}
|
|
||||||
},
|
|
||||||
completion: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
//
|
|
||||||
// ObjectObserverDemoViewController.swift
|
|
||||||
// CoreStoreDemo
|
|
||||||
//
|
|
||||||
// Created by John Rommel Estropia on 2015/05/06.
|
|
||||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import CoreStore
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectObserverDemoViewController
|
|
||||||
|
|
||||||
class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|
||||||
|
|
||||||
var palette: Palette? {
|
|
||||||
|
|
||||||
get {
|
|
||||||
|
|
||||||
return self.monitor?.object
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
|
|
||||||
guard self.monitor?.object != newValue else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let palette = newValue {
|
|
||||||
|
|
||||||
self.monitor = CoreStore.monitorObject(palette)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.monitor = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: NSObject
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
|
|
||||||
self.monitor?.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UIViewController
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
|
|
||||||
if let palette = CoreStore.fetchOne(From(Palette), OrderBy(.Ascending("hue"))) {
|
|
||||||
|
|
||||||
self.monitor = CoreStore.monitorObject(palette)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
CoreStore.beginSynchronous { (transaction) -> Void in
|
|
||||||
|
|
||||||
let palette = transaction.create(Into(Palette))
|
|
||||||
palette.setInitialValues()
|
|
||||||
|
|
||||||
transaction.commitAndWait()
|
|
||||||
}
|
|
||||||
|
|
||||||
let palette = CoreStore.fetchOne(From(Palette), OrderBy(.Ascending("hue")))!
|
|
||||||
self.monitor = CoreStore.monitorObject(palette)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
|
|
||||||
super.viewDidLoad()
|
|
||||||
self.monitor?.addObserver(self)
|
|
||||||
|
|
||||||
if let palette = self.monitor?.object {
|
|
||||||
|
|
||||||
self.reloadPaletteInfo(palette, changedKeys: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ObjectObserver
|
|
||||||
|
|
||||||
func objectMonitor(monitor: ObjectMonitor<Palette>, didUpdateObject object: Palette, changedPersistentKeys: Set<KeyPath>) {
|
|
||||||
|
|
||||||
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
func objectMonitor(monitor: ObjectMonitor<Palette>, didDeleteObject object: Palette) {
|
|
||||||
|
|
||||||
self.navigationItem.rightBarButtonItem?.enabled = false
|
|
||||||
|
|
||||||
self.colorNameLabel?.alpha = 0.3
|
|
||||||
self.colorView?.alpha = 0.3
|
|
||||||
|
|
||||||
self.hsbLabel?.text = "Deleted"
|
|
||||||
self.hsbLabel?.textColor = UIColor.redColor()
|
|
||||||
|
|
||||||
self.hueSlider?.enabled = false
|
|
||||||
self.saturationSlider?.enabled = false
|
|
||||||
self.brightnessSlider?.enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
var monitor: ObjectMonitor<Palette>?
|
|
||||||
|
|
||||||
@IBOutlet weak var colorNameLabel: UILabel?
|
|
||||||
@IBOutlet weak var colorView: UIView?
|
|
||||||
@IBOutlet weak var hsbLabel: UILabel?
|
|
||||||
@IBOutlet weak var dateLabel: UILabel?
|
|
||||||
@IBOutlet weak var hueSlider: UISlider?
|
|
||||||
@IBOutlet weak var saturationSlider: UISlider?
|
|
||||||
@IBOutlet weak var brightnessSlider: UISlider?
|
|
||||||
|
|
||||||
@IBAction dynamic func hueSliderValueDidChange(sender: AnyObject?) {
|
|
||||||
|
|
||||||
let hue = self.hueSlider?.value ?? 0
|
|
||||||
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
|
||||||
|
|
||||||
palette.hue = Int32(hue)
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction dynamic func saturationSliderValueDidChange(sender: AnyObject?) {
|
|
||||||
|
|
||||||
let saturation = self.saturationSlider?.value ?? 0
|
|
||||||
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
|
||||||
|
|
||||||
palette.saturation = saturation
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction dynamic func brightnessSliderValueDidChange(sender: AnyObject?) {
|
|
||||||
|
|
||||||
let brightness = self.brightnessSlider?.value ?? 0
|
|
||||||
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
|
||||||
|
|
||||||
palette.brightness = brightness
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction dynamic func deleteBarButtonTapped(sender: AnyObject?) {
|
|
||||||
|
|
||||||
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
transaction.delete(self?.monitor?.object)
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func reloadPaletteInfo(palette: Palette, changedKeys: Set<String>?) {
|
|
||||||
|
|
||||||
self.colorNameLabel?.text = palette.colorName
|
|
||||||
|
|
||||||
let color = palette.color
|
|
||||||
self.colorNameLabel?.textColor = color
|
|
||||||
self.colorView?.backgroundColor = color
|
|
||||||
|
|
||||||
self.hsbLabel?.text = palette.colorText
|
|
||||||
|
|
||||||
if changedKeys == nil || changedKeys?.contains("hue") == true {
|
|
||||||
|
|
||||||
self.hueSlider?.value = Float(palette.hue)
|
|
||||||
}
|
|
||||||
if changedKeys == nil || changedKeys?.contains("saturation") == true {
|
|
||||||
|
|
||||||
self.saturationSlider?.value = palette.saturation
|
|
||||||
}
|
|
||||||
if changedKeys == nil || changedKeys?.contains("brightness") == true {
|
|
||||||
|
|
||||||
self.brightnessSlider?.value = palette.brightness
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.presentViewController(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
//
|
|
||||||
// CustomLoggerViewController.swift
|
|
||||||
// CoreStoreDemo
|
|
||||||
//
|
|
||||||
// Created by John Rommel Estropia on 2015/06/05.
|
|
||||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import CoreStore
|
|
||||||
import GCDKit
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - CustomLoggerViewController
|
|
||||||
|
|
||||||
class CustomLoggerViewController: UIViewController, CoreStoreLogger {
|
|
||||||
|
|
||||||
// MARK: NSObject
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
|
|
||||||
CoreStore.logger = DefaultLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
let dataStack = DataStack()
|
|
||||||
|
|
||||||
// MARK: UIViewController
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
try! self.dataStack.addStorageAndWait(SQLiteStore(fileName: "emptyStore.sqlite"))
|
|
||||||
CoreStore.logger = self
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(animated: Bool) {
|
|
||||||
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
let alert = UIAlertController(
|
|
||||||
title: "Logger Demo",
|
|
||||||
message: "This demo shows how to plug-in any logging framework to CoreStore.\n\nThe view controller implements CoreStoreLogger and appends all logs to the text view.",
|
|
||||||
preferredStyle: .Alert
|
|
||||||
)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
|
|
||||||
self.presentViewController(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: CoreStoreLogger
|
|
||||||
|
|
||||||
func log(level level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
|
||||||
|
|
||||||
GCDQueue.Main.async { [weak self] in
|
|
||||||
|
|
||||||
let levelString: String
|
|
||||||
switch level {
|
|
||||||
|
|
||||||
case .Trace: levelString = "Trace"
|
|
||||||
case .Notice: levelString = "Notice"
|
|
||||||
case .Warning: levelString = "Warning"
|
|
||||||
case .Fatal: levelString = "Fatal"
|
|
||||||
}
|
|
||||||
self?.textView?.insertText("\((fileName.stringValue as NSString).lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ [Log:\(levelString)] \(message)\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func log(error error: CoreStoreError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
|
||||||
|
|
||||||
GCDQueue.Main.async { [weak self] in
|
|
||||||
|
|
||||||
self?.textView?.insertText("\((fileName.stringValue as NSString).lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ [Error] \(message): \(error)\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assert(@autoclosure condition: () -> Bool, @autoclosure message: () -> String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
|
||||||
|
|
||||||
if condition() {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageString = message()
|
|
||||||
GCDQueue.Main.async { [weak self] in
|
|
||||||
|
|
||||||
self?.textView?.insertText("\((fileName.stringValue as NSString).lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ [Assert] \(messageString)\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
@IBOutlet dynamic weak var textView: UITextView?
|
|
||||||
@IBOutlet dynamic weak var segmentedControl: UISegmentedControl?
|
|
||||||
|
|
||||||
@IBAction dynamic func segmentedControlValueChanged(sender: AnyObject?) {
|
|
||||||
|
|
||||||
switch self.segmentedControl?.selectedSegmentIndex {
|
|
||||||
|
|
||||||
case 0?:
|
|
||||||
self.dataStack.beginAsynchronous { (transaction) -> Void in
|
|
||||||
|
|
||||||
transaction.create(Into(Palette))
|
|
||||||
}
|
|
||||||
|
|
||||||
case 1?:
|
|
||||||
_ = try? dataStack.addStorageAndWait(
|
|
||||||
SQLiteStore(
|
|
||||||
fileName: "emptyStore.sqlite",
|
|
||||||
configuration: "invalidStore"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
case 2?:
|
|
||||||
self.dataStack.beginAsynchronous { (transaction) -> Void in
|
|
||||||
|
|
||||||
transaction.commit()
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,397 +0,0 @@
|
|||||||
//
|
|
||||||
// MigrationsDemoViewController.swift
|
|
||||||
// CoreStoreDemo
|
|
||||||
//
|
|
||||||
// Created by John Rommel Estropia on 2015/06/21.
|
|
||||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import CoreStore
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - MigrationsDemoViewController
|
|
||||||
|
|
||||||
class MigrationsDemoViewController: UIViewController {
|
|
||||||
|
|
||||||
// MARK: UIViewController
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
if let segmentedControl = self.segmentedControl {
|
|
||||||
|
|
||||||
for (index, model) in self.models.enumerate() {
|
|
||||||
|
|
||||||
segmentedControl.setTitle(
|
|
||||||
model.label,
|
|
||||||
forSegmentAtIndex: index
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.setDataStack(nil, model: nil, scrollToSelection: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(animated: Bool) {
|
|
||||||
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
let alert = UIAlertController(
|
|
||||||
title: "Migrations Demo",
|
|
||||||
message: "This demo shows how to run progressive migrations and how to support multiple model versions in a single project.\n\nThe persistent store contains 10000 organisms, which gain/lose properties when the migration evolves/devolves them.\n\nYou can use the \"mutate\" button to change an organism's properties then migrate to a different model to see how its value gets affected.",
|
|
||||||
preferredStyle: .Alert
|
|
||||||
)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
|
|
||||||
self.presentViewController(alert, animated: true, completion: nil)
|
|
||||||
|
|
||||||
|
|
||||||
let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) {
|
|
||||||
(dataStack: DataStack) -> ModelMetadata in
|
|
||||||
|
|
||||||
let models = self.models
|
|
||||||
let migrations = try! dataStack.requiredMigrationsForStorage(
|
|
||||||
SQLiteStore(fileName: "MigrationDemo.sqlite")
|
|
||||||
)
|
|
||||||
|
|
||||||
guard let storeVersion = migrations.first?.sourceVersion else {
|
|
||||||
|
|
||||||
return models.first!
|
|
||||||
}
|
|
||||||
for model in models {
|
|
||||||
|
|
||||||
if model.version == storeVersion {
|
|
||||||
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return models.first!
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectModelVersion(modelMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private typealias ModelMetadata = (label: String, version: String, entityType: AnyClass, migrationChain: MigrationChain)
|
|
||||||
|
|
||||||
private let models: [ModelMetadata] = [
|
|
||||||
(
|
|
||||||
label: "Model V1",
|
|
||||||
version: "MigrationDemo",
|
|
||||||
entityType: OrganismV1.self,
|
|
||||||
migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
label: "Model V2",
|
|
||||||
version: "MigrationDemoV2",
|
|
||||||
entityType: OrganismV2.self,
|
|
||||||
migrationChain: [
|
|
||||||
"MigrationDemo": "MigrationDemoV2",
|
|
||||||
"MigrationDemoV3": "MigrationDemoV2"
|
|
||||||
]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
label: "Model V3",
|
|
||||||
version: "MigrationDemoV3",
|
|
||||||
entityType: OrganismV3.self,
|
|
||||||
migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
private var _listMonitor: ListMonitor<NSManagedObject>?
|
|
||||||
private var listMonitor: ListMonitor<NSManagedObject>? {
|
|
||||||
|
|
||||||
return self._listMonitor
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _dataStack: DataStack?
|
|
||||||
private var dataStack: DataStack? {
|
|
||||||
|
|
||||||
return self._dataStack
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _lastSelectedIndexPath: NSIndexPath?
|
|
||||||
private var lastSelectedIndexPath: NSIndexPath? {
|
|
||||||
|
|
||||||
return self._lastSelectedIndexPath
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setSelectedIndexPath(indexPath: NSIndexPath, scrollToSelection: Bool) {
|
|
||||||
|
|
||||||
self._lastSelectedIndexPath = indexPath
|
|
||||||
self.updateDisplay(reloadData: false, scrollToSelection: scrollToSelection, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet private dynamic weak var headerContainer: UIView?
|
|
||||||
@IBOutlet private dynamic weak var titleLabel: UILabel?
|
|
||||||
@IBOutlet private dynamic weak var organismLabel: UILabel?
|
|
||||||
@IBOutlet private dynamic weak var segmentedControl: UISegmentedControl?
|
|
||||||
@IBOutlet private dynamic weak var progressView: UIProgressView?
|
|
||||||
@IBOutlet private dynamic weak var tableView: UITableView?
|
|
||||||
|
|
||||||
@IBAction private dynamic func segmentedControlValueChanged(sender: AnyObject?) {
|
|
||||||
|
|
||||||
guard let index = self.segmentedControl?.selectedSegmentIndex else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectModelVersion(self.models[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
private func selectModelVersion(model: ModelMetadata) {
|
|
||||||
|
|
||||||
if self.dataStack?.modelVersion == model.version {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setDataStack(nil, model: nil, scrollToSelection: false) // explicitly trigger NSPersistentStore cleanup by deallocating the stack
|
|
||||||
|
|
||||||
let dataStack = DataStack(
|
|
||||||
modelName: "MigrationDemo",
|
|
||||||
migrationChain: model.migrationChain
|
|
||||||
)
|
|
||||||
|
|
||||||
self.setEnabled(false)
|
|
||||||
let progress = dataStack.addStorage(
|
|
||||||
SQLiteStore(fileName: "MigrationDemo.sqlite"),
|
|
||||||
completion: { [weak self] (result) -> Void in
|
|
||||||
|
|
||||||
guard let `self` = self else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .Success = result else {
|
|
||||||
|
|
||||||
self.setEnabled(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setDataStack(dataStack, model: model, scrollToSelection: true)
|
|
||||||
|
|
||||||
let count = dataStack.queryValue(
|
|
||||||
From(model.entityType),
|
|
||||||
Select<Int>(.Count("dna"))
|
|
||||||
)
|
|
||||||
if count > 0 {
|
|
||||||
|
|
||||||
self.setEnabled(true)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
for i: Int64 in 0 ..< 20 {
|
|
||||||
|
|
||||||
dataStack.beginAsynchronous { (transaction) -> Void in
|
|
||||||
|
|
||||||
for j: Int64 in 0 ..< 500 {
|
|
||||||
|
|
||||||
let organism = transaction.create(Into(model.entityType)) as! OrganismProtocol
|
|
||||||
organism.dna = (i * 500) + j + 1
|
|
||||||
organism.mutate()
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataStack.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
transaction.commit { _ in
|
|
||||||
|
|
||||||
self?.setEnabled(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if let progress = progress {
|
|
||||||
|
|
||||||
progress.setProgressHandler { [weak self] (progress) -> Void in
|
|
||||||
|
|
||||||
self?.reloadTableHeaderWithProgress(progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setEnabled(enabled: Bool) {
|
|
||||||
|
|
||||||
UIView.animateWithDuration(
|
|
||||||
0.2,
|
|
||||||
delay: 0,
|
|
||||||
options: .BeginFromCurrentState,
|
|
||||||
animations: { () -> Void in
|
|
||||||
|
|
||||||
let navigationItem = self.navigationItem
|
|
||||||
navigationItem.leftBarButtonItem?.enabled = enabled
|
|
||||||
navigationItem.rightBarButtonItem?.enabled = enabled
|
|
||||||
navigationItem.hidesBackButton = !enabled
|
|
||||||
|
|
||||||
self.segmentedControl?.enabled = enabled
|
|
||||||
|
|
||||||
if let tableView = self.tableView {
|
|
||||||
|
|
||||||
tableView.alpha = enabled ? 1.0 : 0.5
|
|
||||||
tableView.userInteractionEnabled = enabled
|
|
||||||
}
|
|
||||||
},
|
|
||||||
completion: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setDataStack(dataStack: DataStack?, model: ModelMetadata?, scrollToSelection: Bool) {
|
|
||||||
|
|
||||||
if let dataStack = dataStack, let model = model {
|
|
||||||
|
|
||||||
self.segmentedControl?.selectedSegmentIndex = self.models.map { $0.version }.indexOf(model.version)!
|
|
||||||
|
|
||||||
self._dataStack = dataStack
|
|
||||||
let listMonitor = dataStack.monitorList(From(model.entityType), OrderBy(.Descending("dna")))
|
|
||||||
listMonitor.addObserver(self)
|
|
||||||
self._listMonitor = listMonitor
|
|
||||||
|
|
||||||
if self.lastSelectedIndexPath == nil {
|
|
||||||
|
|
||||||
if listMonitor.numberOfObjectsInSection(0) > 0 {
|
|
||||||
|
|
||||||
self.setSelectedIndexPath(NSIndexPath(forRow: 0, inSection: 0), scrollToSelection: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.segmentedControl?.selectedSegmentIndex = UISegmentedControlNoSegment
|
|
||||||
self._listMonitor = nil
|
|
||||||
self._dataStack = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.updateDisplay(reloadData: true, scrollToSelection: scrollToSelection, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func reloadTableHeaderWithProgress(progress: NSProgress) {
|
|
||||||
|
|
||||||
self.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
|
|
||||||
self.titleLabel?.text = "Migrating: \(progress.localizedDescription)"
|
|
||||||
self.organismLabel?.text = "Progressive step \(progress.localizedAdditionalDescription)"
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateDisplay(reloadData reloadData: Bool, scrollToSelection: Bool, animated: Bool) {
|
|
||||||
|
|
||||||
var lines = [String]()
|
|
||||||
var organismType = ""
|
|
||||||
if let indexPath = self.lastSelectedIndexPath, let organism = self.listMonitor?[indexPath] {
|
|
||||||
|
|
||||||
for property in organism.entity.properties {
|
|
||||||
|
|
||||||
let value: AnyObject = organism.valueForKey(property.name) ?? NSNull()
|
|
||||||
lines.append("\(property.name): \(value)")
|
|
||||||
}
|
|
||||||
organismType = organism.entity.managedObjectClassName
|
|
||||||
}
|
|
||||||
|
|
||||||
self.titleLabel?.text = organismType
|
|
||||||
self.organismLabel?.text = lines.joinWithSeparator("\n")
|
|
||||||
self.progressView?.progress = 0
|
|
||||||
|
|
||||||
self.headerContainer?.setNeedsLayout()
|
|
||||||
|
|
||||||
guard let tableView = self.tableView else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if reloadData {
|
|
||||||
|
|
||||||
tableView.reloadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
tableView.layoutIfNeeded()
|
|
||||||
|
|
||||||
if let indexPath = self.lastSelectedIndexPath where indexPath.row < tableView.numberOfRowsInSection(0) {
|
|
||||||
|
|
||||||
tableView.selectRowAtIndexPath(indexPath,
|
|
||||||
animated: scrollToSelection && animated,
|
|
||||||
scrollPosition: scrollToSelection ? .Middle : .None
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - MigrationsDemoViewController: ListObserver
|
|
||||||
|
|
||||||
extension MigrationsDemoViewController: ListObserver {
|
|
||||||
|
|
||||||
// MARK: ListObserver
|
|
||||||
|
|
||||||
func listMonitorWillChange(monitor: ListMonitor<NSManagedObject>) { }
|
|
||||||
|
|
||||||
func listMonitorDidChange(monitor: ListMonitor<NSManagedObject>) {
|
|
||||||
|
|
||||||
if self.lastSelectedIndexPath == nil && self.listMonitor?.numberOfObjectsInSection(0) > 0 {
|
|
||||||
|
|
||||||
self.tableView?.reloadData()
|
|
||||||
self.setSelectedIndexPath(NSIndexPath(forRow: 0, inSection: 0), scrollToSelection: false)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.updateDisplay(reloadData: true, scrollToSelection: true, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - MigrationsDemoViewController: UITableViewDataSource, UITableViewDelegate
|
|
||||||
|
|
||||||
extension MigrationsDemoViewController: UITableViewDataSource, UITableViewDelegate {
|
|
||||||
|
|
||||||
// MARK: UITableViewDataSource
|
|
||||||
|
|
||||||
@objc dynamic func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
|
|
||||||
return self.listMonitor?.numberOfObjectsInSection(0) ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc dynamic func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCellWithIdentifier("OrganismTableViewCell", forIndexPath: indexPath) as! OrganismTableViewCell
|
|
||||||
|
|
||||||
let dna = (self.listMonitor?[indexPath] as? OrganismProtocol)?.dna.description ?? ""
|
|
||||||
cell.dnaLabel?.text = "DNA: \(dna)"
|
|
||||||
cell.mutateButtonHandler = { [weak self] _ -> Void in
|
|
||||||
|
|
||||||
guard let `self` = self,
|
|
||||||
let dataStack = self.dataStack,
|
|
||||||
let organism = self.listMonitor?[indexPath] else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setSelectedIndexPath(indexPath, scrollToSelection: false)
|
|
||||||
self.setEnabled(false)
|
|
||||||
dataStack.beginAsynchronous { [weak self] (transaction) -> Void in
|
|
||||||
|
|
||||||
let organism = transaction.edit(organism) as! OrganismProtocol
|
|
||||||
organism.mutate()
|
|
||||||
|
|
||||||
transaction.commit { _ -> Void in
|
|
||||||
|
|
||||||
self?.setEnabled(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDelegate
|
|
||||||
|
|
||||||
@objc dynamic func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
self.setSelectedIndexPath(indexPath, scrollToSelection: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// OrganismV2ToV3MigrationPolicy.swift
|
|
||||||
// CoreStoreDemo
|
|
||||||
//
|
|
||||||
// Created by John Rommel Estropia on 2015/06/27.
|
|
||||||
// Copyright © 2015 John Rommel Estropia. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
class OrganismV2ToV3MigrationPolicy: NSEntityMigrationPolicy {
|
|
||||||
|
|
||||||
override func createDestinationInstancesForSourceInstance(sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
|
|
||||||
try super.createDestinationInstancesForSourceInstance(sInstance, entityMapping: mapping, manager: manager)
|
|
||||||
|
|
||||||
for dInstance in manager.destinationInstancesForEntityMappingNamed(mapping.name, sourceInstances: [sInstance]) {
|
|
||||||
|
|
||||||
dInstance.setValue(false, forKey: "hasVertebrae")
|
|
||||||
dInstance.setValue(sInstance.valueForKey("numberOfFlippers"), forKey: "numberOfLimbs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
|
|
||||||
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV1" syncable="YES">
|
|
||||||
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
|
|
||||||
<attribute name="hasHead" optional="YES" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="hasTail" optional="YES" attributeType="Boolean" syncable="YES"/>
|
|
||||||
</entity>
|
|
||||||
<elements>
|
|
||||||
<element name="Organism" positionX="-36" positionY="9" width="128" height="90"/>
|
|
||||||
</elements>
|
|
||||||
</model>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
|
|
||||||
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV2" syncable="YES">
|
|
||||||
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
|
|
||||||
<attribute name="hasHead" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="hasTail" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="numberOfFlippers" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
|
|
||||||
</entity>
|
|
||||||
<elements>
|
|
||||||
<element name="Organism" positionX="-36" positionY="9" width="128" height="105"/>
|
|
||||||
</elements>
|
|
||||||
</model>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14E46" minimumToolsVersion="Xcode 4.3">
|
|
||||||
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV3" syncable="YES">
|
|
||||||
<attribute name="dna" optional="YES" attributeType="Integer 64" syncable="YES"/>
|
|
||||||
<attribute name="hasHead" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="hasTail" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="hasVertebrae" attributeType="Boolean" syncable="YES"/>
|
|
||||||
<attribute name="numberOfLimbs" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
|
|
||||||
</entity>
|
|
||||||
<elements>
|
|
||||||
<element name="Organism" positionX="-36" positionY="9" width="128" height="120"/>
|
|
||||||
</elements>
|
|
||||||
</model>
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// BaseTestCase.swift
|
// BaseTestCase.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -36,11 +36,11 @@ class BaseTestCase: XCTestCase {
|
|||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
func prepareStack<T>(configurations configurations: [String?] = [nil], @noescape _ closure: (dataStack: DataStack) -> T) -> T {
|
func prepareStack(configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) throws -> Void) {
|
||||||
|
|
||||||
let stack = DataStack(
|
let stack = DataStack(
|
||||||
modelName: "Model",
|
xcodeModelName: "Model",
|
||||||
bundle: NSBundle(forClass: self.dynamicType)
|
bundle: Bundle(for: Self.self)
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -49,46 +49,76 @@ class BaseTestCase: XCTestCase {
|
|||||||
try stack.addStorageAndWait(
|
try stack.addStorageAndWait(
|
||||||
SQLiteStore(
|
SQLiteStore(
|
||||||
fileURL: SQLiteStore.defaultRootDirectory
|
fileURL: SQLiteStore.defaultRootDirectory
|
||||||
.URLByAppendingPathComponent(NSUUID().UUIDString)
|
.appendingPathComponent(UUID().uuidString)
|
||||||
.URLByAppendingPathComponent("\(self.dynamicType)_\(($0 ?? "-null-")).sqlite"),
|
.appendingPathComponent("\(Self.self)_\(($0 ?? "-null-")).sqlite"),
|
||||||
configuration: $0,
|
configuration: $0,
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
try closure(stack)
|
||||||
}
|
}
|
||||||
catch let error as NSError {
|
catch let error as NSError {
|
||||||
|
|
||||||
XCTFail(error.coreStoreDumpString)
|
XCTFail(error.coreStoreDumpString)
|
||||||
}
|
}
|
||||||
return closure(dataStack: stack)
|
self.addTeardownBlock {
|
||||||
|
stack.unsafeRemoveAllPersistentStoresAndWait()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
func expectLogger<T>(expectations: [TestLogger.Expectation], @noescape 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 {
|
defer {
|
||||||
|
|
||||||
self.checkExpectationsImmediately()
|
self.checkExpectationsImmediately()
|
||||||
CoreStore.logger = TestLogger([:])
|
CoreStoreDefaults.logger = TestLogger([:])
|
||||||
}
|
}
|
||||||
return closure()
|
return try closure()
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
func expectLogger(expectations: [TestLogger.Expectation: XCTestExpectation]) {
|
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
|
@nonobjc
|
||||||
func prepareLoggerExpectations(expectations: [TestLogger.Expectation]) -> [TestLogger.Expectation: XCTestExpectation] {
|
func prepareLoggerExpectations(_ expectations: [TestLogger.Expectation]) -> [TestLogger.Expectation: XCTestExpectation] {
|
||||||
|
|
||||||
var testExpectations: [TestLogger.Expectation: XCTestExpectation] = [:]
|
var testExpectations: [TestLogger.Expectation: XCTestExpectation] = [:]
|
||||||
for expectation in expectations {
|
for expectation in expectations {
|
||||||
|
|
||||||
testExpectations[expectation] = self.expectationWithDescription("Logger Expectation: \(expectation)")
|
testExpectations[expectation] = self.expectation(description: "Logger Expectation: \(expectation)")
|
||||||
}
|
}
|
||||||
return testExpectations
|
return testExpectations
|
||||||
}
|
}
|
||||||
@@ -96,13 +126,13 @@ class BaseTestCase: XCTestCase {
|
|||||||
@nonobjc
|
@nonobjc
|
||||||
func checkExpectationsImmediately() {
|
func checkExpectationsImmediately() {
|
||||||
|
|
||||||
self.waitForExpectationsWithTimeout(0, handler: nil)
|
self.waitForExpectations(timeout: 0, handler: { _ in })
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
func waitAndCheckExpectations() {
|
func waitAndCheckExpectations() {
|
||||||
|
|
||||||
self.waitForExpectationsWithTimeout(10, handler: nil)
|
self.waitForExpectations(timeout: 10, handler: {_ in })
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: XCTestCase
|
// MARK: XCTestCase
|
||||||
@@ -111,12 +141,12 @@ class BaseTestCase: XCTestCase {
|
|||||||
|
|
||||||
super.setUp()
|
super.setUp()
|
||||||
self.deleteStores()
|
self.deleteStores()
|
||||||
CoreStore.logger = TestLogger([:])
|
CoreStoreDefaults.logger = TestLogger([:])
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
|
|
||||||
CoreStore.logger = DefaultLogger()
|
CoreStoreDefaults.logger = DefaultLogger()
|
||||||
self.deleteStores()
|
self.deleteStores()
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
@@ -126,7 +156,7 @@ class BaseTestCase: XCTestCase {
|
|||||||
|
|
||||||
private func deleteStores() {
|
private func deleteStores() {
|
||||||
|
|
||||||
_ = try? NSFileManager.defaultManager().removeItemAtURL(SQLiteStore.defaultRootDirectory)
|
_ = try? FileManager.default.removeItem(at: SQLiteStore.defaultRootDirectory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +167,11 @@ class TestLogger: CoreStoreLogger {
|
|||||||
|
|
||||||
enum Expectation {
|
enum Expectation {
|
||||||
|
|
||||||
case LogWarning
|
case logWarning
|
||||||
case LogFatal
|
case logFatal
|
||||||
case LogError
|
case logError
|
||||||
case AssertionFailure
|
case assertionFailure
|
||||||
case FatalError
|
case fatalError
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ expectations: [Expectation: XCTestExpectation]) {
|
init(_ expectations: [Expectation: XCTestExpectation]) {
|
||||||
@@ -152,33 +182,35 @@ class TestLogger: CoreStoreLogger {
|
|||||||
|
|
||||||
// MARK: CoreStoreLogger
|
// MARK: CoreStoreLogger
|
||||||
|
|
||||||
func log(level level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
var enableObjectConcurrencyDebugging: Bool = true
|
||||||
|
|
||||||
|
func log(level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
switch level {
|
switch level {
|
||||||
|
|
||||||
case .Warning: self.fulfill(.LogWarning)
|
case .warning: self.fulfill(.logWarning)
|
||||||
case .Fatal: self.fulfill(.LogFatal)
|
case .fatal: self.fulfill(.logFatal)
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func log(error error: CoreStoreError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
func log(error: CoreStoreError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
self.fulfill(.LogError)
|
self.fulfill(.logError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assert(@autoclosure condition: () -> Bool, @autoclosure message: () -> String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
func assert(_ condition: @autoclosure () -> Bool, message: @autoclosure () -> String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
if condition() {
|
if condition() {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.fulfill(.AssertionFailure)
|
self.fulfill(.assertionFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func abort(message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
func abort(_ message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
self.fulfill(.FatalError)
|
self.fulfill(.fatalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -186,7 +218,7 @@ class TestLogger: CoreStoreLogger {
|
|||||||
|
|
||||||
private var expectations: [Expectation: XCTestExpectation]
|
private var expectations: [Expectation: XCTestExpectation]
|
||||||
|
|
||||||
private func fulfill(expectation: Expectation) {
|
private func fulfill(_ expectation: Expectation) {
|
||||||
|
|
||||||
if let instance = self.expectations[expectation] {
|
if let instance = self.expectations[expectation] {
|
||||||
|
|
||||||
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: NSDateFormatter = {
|
|
||||||
|
|
||||||
let formatter = NSDateFormatter()
|
|
||||||
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = NSTimeZone(name: "UTC")
|
|
||||||
formatter.calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
|
|
||||||
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.enumerate() {
|
|
||||||
|
|
||||||
let configurationOrdinal = configurationIndex + 1
|
|
||||||
if configuration == nil || configuration == "Config1" {
|
|
||||||
|
|
||||||
for idIndex in 1 ... 5 {
|
|
||||||
|
|
||||||
let object = transaction.create(Into<TestEntity1>(configuration))
|
|
||||||
object.testEntityID = NSNumber(integer: (configurationOrdinal * 100) + idIndex)
|
|
||||||
|
|
||||||
object.testNumber = idIndex
|
|
||||||
object.testDate = self.dateFormatter.dateFromString("2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
|
||||||
object.testBoolean = (idIndex % 2) == 1
|
|
||||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
|
||||||
|
|
||||||
let string = "\(configuration ?? "nil"):TestEntity1:\(idIndex)"
|
|
||||||
object.testString = string
|
|
||||||
object.testData = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if configuration == nil || configuration == "Config2" {
|
|
||||||
|
|
||||||
for idIndex in 1 ... 5 {
|
|
||||||
|
|
||||||
let object = transaction.create(Into<TestEntity2>(configuration))
|
|
||||||
object.testEntityID = NSNumber(integer: (configurationOrdinal * 200) + idIndex)
|
|
||||||
|
|
||||||
object.testNumber = idIndex
|
|
||||||
object.testDate = self.dateFormatter.dateFromString("2000-\(configurationOrdinal)-\(idIndex)T00:00:00Z")
|
|
||||||
object.testBoolean = (idIndex % 2) == 1
|
|
||||||
object.testDecimal = NSDecimalNumber(string: "\(idIndex)")
|
|
||||||
|
|
||||||
let string = "\(configuration ?? "nil"):TestEntity2:\(idIndex)"
|
|
||||||
object.testString = string
|
|
||||||
object.testData = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transaction.commitAndWait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// BridgingTests.h
|
// BridgingTests.h
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// BridgingTests.m
|
// BridgingTests.m
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,6 +30,10 @@
|
|||||||
|
|
||||||
@import CoreData;
|
@import CoreData;
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
|
|
||||||
// MARK: - BridgingTests
|
// MARK: - BridgingTests
|
||||||
|
|
||||||
@implementation BridgingTests
|
@implementation BridgingTests
|
||||||
@@ -163,19 +167,13 @@
|
|||||||
- (void)test_ThatDataStacks_BridgeCorrectly {
|
- (void)test_ThatDataStacks_BridgeCorrectly {
|
||||||
|
|
||||||
CSDataStack *dataStack = [[CSDataStack alloc]
|
CSDataStack *dataStack = [[CSDataStack alloc]
|
||||||
initWithModelName:@"Model"
|
initWithXcodeModelName:@"Model"
|
||||||
bundle:[NSBundle bundleForClass:[self class]]
|
bundle:[NSBundle bundleForClass:[self class]]
|
||||||
versionChain:nil];
|
versionChain:nil];
|
||||||
XCTAssertNotNil(dataStack);
|
XCTAssertNotNil(dataStack);
|
||||||
|
|
||||||
[CSCoreStore setDefaultStack:dataStack];
|
|
||||||
XCTAssertTrue([dataStack isEqual:[CSCoreStore defaultStack]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)test_ThatStorages_BridgeCorrectly {
|
|
||||||
|
|
||||||
NSError *memoryError;
|
NSError *memoryError;
|
||||||
CSInMemoryStore *memoryStorage = [CSCoreStore
|
CSInMemoryStore *memoryStorage = [dataStack
|
||||||
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
||||||
error:&memoryError];
|
error:&memoryError];
|
||||||
XCTAssertNotNil(memoryStorage);
|
XCTAssertNotNil(memoryStorage);
|
||||||
@@ -186,15 +184,78 @@
|
|||||||
XCTAssertNil(memoryError);
|
XCTAssertNil(memoryError);
|
||||||
|
|
||||||
NSError *sqliteError;
|
NSError *sqliteError;
|
||||||
CSSQLiteStore *sqliteStorage = [CSCoreStore
|
CSSQLiteStore *sqliteStorage = [dataStack
|
||||||
addSQLiteStorageAndWait:[CSSQLiteStore new]
|
addSQLiteStorageAndWait:[CSSQLiteStore new]
|
||||||
error:&sqliteError];
|
error:&sqliteError];
|
||||||
XCTAssertNotNil(sqliteStorage);
|
XCTAssertNotNil(sqliteStorage);
|
||||||
XCTAssertEqualObjects([[sqliteStorage class] storeType], [CSSQLiteStore storeType]);
|
XCTAssertEqualObjects([[sqliteStorage class] storeType], [CSSQLiteStore storeType]);
|
||||||
XCTAssertEqualObjects([[sqliteStorage class] storeType], NSSQLiteStoreType);
|
XCTAssertEqualObjects([[sqliteStorage class] storeType], NSSQLiteStoreType);
|
||||||
XCTAssertNil(sqliteStorage.configuration);
|
XCTAssertNil(sqliteStorage.configuration);
|
||||||
XCTAssertEqualObjects(sqliteStorage.storeOptions, @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" } });
|
NSDictionary *storeOptions = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" },
|
||||||
|
NSBinaryStoreInsecureDecodingCompatibilityOption: @YES };
|
||||||
|
XCTAssertEqualObjects(sqliteStorage.storeOptions, storeOptions);
|
||||||
XCTAssertNil(sqliteError);
|
XCTAssertNil(sqliteError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)test_ThatTransactions_BridgeCorrectly {
|
||||||
|
|
||||||
|
CSDataStack *dataStack = [[CSDataStack alloc]
|
||||||
|
initWithXcodeModelName:@"Model"
|
||||||
|
bundle:[NSBundle bundleForClass:[self class]]
|
||||||
|
versionChain:nil];
|
||||||
|
XCTAssertNotNil(dataStack);
|
||||||
|
|
||||||
|
[dataStack
|
||||||
|
addInMemoryStorageAndWait:[CSInMemoryStore new]
|
||||||
|
error:nil];
|
||||||
|
|
||||||
|
{
|
||||||
|
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"];
|
||||||
|
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"];
|
||||||
|
[dataStack beginAsynchronous:^(CSAsynchronousDataTransaction * _Nonnull transaction) {
|
||||||
|
|
||||||
|
XCTAssertNotNil(transaction);
|
||||||
|
XCTAssert([transaction isKindOfClass:[CSAsynchronousDataTransaction class]]);
|
||||||
|
[transaction
|
||||||
|
commitWithSuccess:^{
|
||||||
|
|
||||||
|
[expectation fulfill];
|
||||||
|
}
|
||||||
|
failure:^(CSError *error){
|
||||||
|
|
||||||
|
XCTFail();
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|||||||
93
CoreStoreTests/ConvenienceTests.swift
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
//
|
||||||
|
// ConvenienceTests.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 CoreData
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable
|
||||||
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - ConvenienceTests
|
||||||
|
|
||||||
|
class ConvenienceTests: BaseTestCase {
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func test_ThatDataStacks_CanCreateFetchedResultsControllers() {
|
||||||
|
|
||||||
|
self.prepareStack { (stack) in
|
||||||
|
|
||||||
|
let controller = stack.createFetchedResultsController(
|
||||||
|
From<TestEntity1>(),
|
||||||
|
SectionBy(#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)
|
||||||
|
XCTAssertEqual(controller.fetchRequest.entity?.managedObjectClassName, NSStringFromClass(TestEntity1.self))
|
||||||
|
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
|
||||||
|
XCTAssertEqual(
|
||||||
|
controller.fetchRequest.sortDescriptors!,
|
||||||
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
controller.fetchRequest.predicate,
|
||||||
|
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||||
|
)
|
||||||
|
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func test_ThatUnsafeDataTransactions_CanCreateFetchedResultsControllers() {
|
||||||
|
|
||||||
|
self.prepareStack { (stack) in
|
||||||
|
|
||||||
|
withExtendedLifetime(stack.beginUnsafe()) { (transaction: UnsafeDataTransaction) in
|
||||||
|
|
||||||
|
let controller = transaction.createFetchedResultsController(
|
||||||
|
From<TestEntity1>(),
|
||||||
|
SectionBy(#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)
|
||||||
|
XCTAssertEqual(controller.fetchRequest.entity?.managedObjectClassName, NSStringFromClass(TestEntity1.self))
|
||||||
|
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
|
||||||
|
XCTAssertEqual(
|
||||||
|
controller.fetchRequest.sortDescriptors!,
|
||||||
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
controller.fetchRequest.predicate,
|
||||||
|
Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
|
||||||
|
)
|
||||||
|
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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", indexes: [[\Dog.$nickname, \Dog.$age]]),
|
||||||
|
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
|
// ErrorTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,38 +31,39 @@ import CoreStore
|
|||||||
|
|
||||||
// MARK: - ErrorTests
|
// MARK: - ErrorTests
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||||
final class ErrorTests: XCTestCase {
|
final class ErrorTests: XCTestCase {
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatUnknownErrors_BridgeCorrectly() {
|
dynamic func test_ThatUnknownErrors_BridgeCorrectly() {
|
||||||
|
|
||||||
let error = CoreStoreError.Unknown
|
let error = CoreStoreError.unknown
|
||||||
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.UnknownError.rawValue)
|
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.unknownError.rawValue)
|
||||||
|
|
||||||
let userInfo: NSDictionary = [:]
|
let userInfo: NSDictionary = [:]
|
||||||
|
|
||||||
let objcError = error.bridgeToObjectiveC
|
let objcError = error.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError.bridgeToSwift)
|
XCTAssertEqual(error, objcError.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError.code, CoreStoreErrorCode.UnknownError.rawValue)
|
XCTAssertEqual(objcError.code, CoreStoreErrorCode.unknownError.rawValue)
|
||||||
XCTAssertEqual(objcError.userInfo, userInfo)
|
XCTAssertEqual(objcError.userInfo as NSDictionary, userInfo)
|
||||||
|
|
||||||
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.UnknownError.rawValue)
|
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.unknownError.rawValue)
|
||||||
XCTAssertEqual(objcError2.userInfo, userInfo)
|
XCTAssertEqual(objcError2.userInfo as NSDictionary, userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatDifferentStorageExistsAtURLErrors_BridgeCorrectly() {
|
dynamic func test_ThatDifferentStorageExistsAtURLErrors_BridgeCorrectly() {
|
||||||
|
|
||||||
let dummyURL = NSURL(string: "file:///test1/test2.sqlite")!
|
let dummyURL = URL(string: "file:///test1/test2.sqlite")!
|
||||||
|
|
||||||
let error = CoreStoreError.DifferentStorageExistsAtURL(existingPersistentStoreURL: dummyURL)
|
let error = CoreStoreError.differentStorageExistsAtURL(existingPersistentStoreURL: dummyURL)
|
||||||
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.DifferentPersistentStoreExistsAtURL.rawValue)
|
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.differentStorageExistsAtURL.rawValue)
|
||||||
|
|
||||||
let userInfo: NSDictionary = [
|
let userInfo: NSDictionary = [
|
||||||
"existingPersistentStoreURL": dummyURL
|
"existingPersistentStoreURL": dummyURL
|
||||||
@@ -70,54 +71,59 @@ final class ErrorTests: XCTestCase {
|
|||||||
let objcError = error.bridgeToObjectiveC
|
let objcError = error.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError.bridgeToSwift)
|
XCTAssertEqual(error, objcError.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError.code, CoreStoreErrorCode.DifferentPersistentStoreExistsAtURL.rawValue)
|
XCTAssertEqual(objcError.code, CoreStoreErrorCode.differentStorageExistsAtURL.rawValue)
|
||||||
XCTAssertEqual(objcError.userInfo, userInfo)
|
XCTAssertEqual(objcError.userInfo as NSDictionary, userInfo)
|
||||||
|
|
||||||
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.DifferentPersistentStoreExistsAtURL.rawValue)
|
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.differentStorageExistsAtURL.rawValue)
|
||||||
XCTAssertEqual(objcError2.userInfo, userInfo)
|
XCTAssertEqual(objcError2.userInfo as NSDictionary, userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatMappingModelNotFoundErrors_BridgeCorrectly() {
|
dynamic func test_ThatMappingModelNotFoundErrors_BridgeCorrectly() {
|
||||||
|
|
||||||
let dummyURL = NSURL(string: "file:///test1/test2.sqlite")!
|
let dummyURL = URL(string: "file:///test1/test2.sqlite")!
|
||||||
|
|
||||||
let model = NSManagedObjectModel.fromBundle(NSBundle(forClass: self.dynamicType), modelName: "Model")
|
let schemaHistory = SchemaHistory(
|
||||||
|
XcodeDataModelSchema.from(
|
||||||
|
modelName: "Model",
|
||||||
|
bundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
)
|
||||||
let version = "1.0.0"
|
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).domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.MappingModelNotFound.rawValue)
|
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.mappingModelNotFound.rawValue)
|
||||||
|
|
||||||
let userInfo: NSDictionary = [
|
let userInfo: NSDictionary = [
|
||||||
"localStoreURL": dummyURL,
|
"localStoreURL": dummyURL,
|
||||||
"targetModel": model,
|
"targetModel": schemaHistory.rawModel,
|
||||||
"targetModelVersion": version
|
"targetModelVersion": version
|
||||||
]
|
]
|
||||||
let objcError = error.bridgeToObjectiveC
|
let objcError = error.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError.bridgeToSwift)
|
XCTAssertEqual(error, objcError.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError.code, CoreStoreErrorCode.MappingModelNotFound.rawValue)
|
XCTAssertEqual(objcError.code, CoreStoreErrorCode.mappingModelNotFound.rawValue)
|
||||||
XCTAssertEqual(objcError.userInfo, userInfo)
|
XCTAssertEqual(objcError.userInfo as NSDictionary, userInfo)
|
||||||
|
|
||||||
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.MappingModelNotFound.rawValue)
|
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.mappingModelNotFound.rawValue)
|
||||||
XCTAssertEqual(objcError2.userInfo, userInfo)
|
XCTAssertEqual(objcError2.userInfo as NSDictionary, userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatProgressiveMigrationRequiredErrors_BridgeCorrectly() {
|
dynamic func test_ThatProgressiveMigrationRequiredErrors_BridgeCorrectly() {
|
||||||
|
|
||||||
let dummyURL = NSURL(string: "file:///test1/test2.sqlite")!
|
let dummyURL = URL(string: "file:///test1/test2.sqlite")!
|
||||||
|
|
||||||
let error = CoreStoreError.ProgressiveMigrationRequired(localStoreURL: dummyURL)
|
let error = CoreStoreError.progressiveMigrationRequired(localStoreURL: dummyURL)
|
||||||
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.ProgressiveMigrationRequired.rawValue)
|
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.progressiveMigrationRequired.rawValue)
|
||||||
|
|
||||||
let userInfo: NSDictionary = [
|
let userInfo: NSDictionary = [
|
||||||
"localStoreURL": dummyURL
|
"localStoreURL": dummyURL
|
||||||
@@ -125,14 +131,14 @@ final class ErrorTests: XCTestCase {
|
|||||||
let objcError = error.bridgeToObjectiveC
|
let objcError = error.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError.bridgeToSwift)
|
XCTAssertEqual(error, objcError.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError.code, CoreStoreErrorCode.ProgressiveMigrationRequired.rawValue)
|
XCTAssertEqual(objcError.code, CoreStoreErrorCode.progressiveMigrationRequired.rawValue)
|
||||||
XCTAssertEqual(objcError.userInfo, userInfo)
|
XCTAssertEqual(objcError.userInfo as NSDictionary, userInfo)
|
||||||
|
|
||||||
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.ProgressiveMigrationRequired.rawValue)
|
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.progressiveMigrationRequired.rawValue)
|
||||||
XCTAssertEqual(objcError2.userInfo, userInfo)
|
XCTAssertEqual(objcError2.userInfo as NSDictionary, userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -144,12 +150,12 @@ final class ErrorTests: XCTestCase {
|
|||||||
userInfo: [
|
userInfo: [
|
||||||
"key1": "value1",
|
"key1": "value1",
|
||||||
"key2": 2,
|
"key2": 2,
|
||||||
"key3": NSDate()
|
"key3": Date()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
let error = CoreStoreError(internalError)
|
let error = CoreStoreError(internalError)
|
||||||
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.InternalError.rawValue)
|
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.internalError.rawValue)
|
||||||
|
|
||||||
let userInfo: NSDictionary = [
|
let userInfo: NSDictionary = [
|
||||||
"NSError": internalError
|
"NSError": internalError
|
||||||
@@ -157,13 +163,13 @@ final class ErrorTests: XCTestCase {
|
|||||||
let objcError = error.bridgeToObjectiveC
|
let objcError = error.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError.bridgeToSwift)
|
XCTAssertEqual(error, objcError.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError.code, CoreStoreErrorCode.InternalError.rawValue)
|
XCTAssertEqual(objcError.code, CoreStoreErrorCode.internalError.rawValue)
|
||||||
XCTAssertEqual(objcError.userInfo, userInfo)
|
XCTAssertEqual(objcError.userInfo as NSDictionary, userInfo)
|
||||||
|
|
||||||
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
let objcError2 = objcError.bridgeToSwift.bridgeToObjectiveC
|
||||||
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
XCTAssertEqual(error, objcError2.bridgeToSwift)
|
||||||
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
XCTAssertEqual(objcError2.domain, CoreStoreErrorDomain)
|
||||||
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.InternalError.rawValue)
|
XCTAssertEqual(objcError2.code, CoreStoreErrorCode.internalError.rawValue)
|
||||||
XCTAssertEqual(objcError2.userInfo, userInfo)
|
XCTAssertEqual(objcError2.userInfo as NSDictionary, userInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// FromTests.swift
|
// FromTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -38,7 +39,7 @@ final class FromTests: BaseTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From()
|
let from = From<NSManagedObject>()
|
||||||
XCTAssert(from.entityClass === NSManagedObject.self)
|
XCTAssert(from.entityClass === NSManagedObject.self)
|
||||||
XCTAssertNil(from.configurations)
|
XCTAssertNil(from.configurations)
|
||||||
}
|
}
|
||||||
@@ -74,33 +75,31 @@ final class FromTests: BaseTestCase {
|
|||||||
|
|
||||||
let from = From<TestEntity1>()
|
let from = From<TestEntity1>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config1")
|
let from = From<TestEntity1>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,102 +114,98 @@ final class FromTests: BaseTestCase {
|
|||||||
|
|
||||||
let from = From<TestEntity1>()
|
let from = From<TestEntity1>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config1")
|
let from = From<TestEntity1>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config2")
|
let from = From<TestEntity1>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>()
|
let from = From<TestEntity2>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config1")
|
let from = From<TestEntity2>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config2")
|
let from = From<TestEntity2>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,99 +220,96 @@ final class FromTests: BaseTestCase {
|
|||||||
|
|
||||||
let from = From<TestEntity1>()
|
let from = From<TestEntity1>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(Set(affectedConfigurations), ["PF_DEFAULT_CONFIGURATION_NAME", "Config1"] as Set)
|
XCTAssertEqual(Set(affectedConfigurations), ["PF_DEFAULT_CONFIGURATION_NAME", "Config1"] as Set)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config1")
|
let from = From<TestEntity1>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config2")
|
let from = From<TestEntity1>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>()
|
let from = From<TestEntity2>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config1")
|
let from = From<TestEntity2>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config2")
|
let from = From<TestEntity2>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,96 +324,94 @@ final class FromTests: BaseTestCase {
|
|||||||
|
|
||||||
let from = From<TestEntity1>()
|
let from = From<TestEntity1>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config1")
|
let from = From<TestEntity1>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
XCTAssertEqual(affectedConfigurations, ["Config1"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity1>("Config2")
|
let from = From<TestEntity1>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>()
|
let from = From<TestEntity2>()
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config1")
|
let from = From<TestEntity2>("Config1")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = self.expectLogger([.LogWarning]) {
|
self.expectError(code: .persistentStoreNotFound) {
|
||||||
|
|
||||||
from.applyToFetchRequest(request, context: dataStack.mainContext)
|
try from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
}
|
}
|
||||||
XCTAssertFalse(storesFound)
|
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertTrue(affectedConfigurations.isEmpty)
|
XCTAssertTrue(affectedConfigurations.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let from = From<TestEntity2>("Config2")
|
let from = From<TestEntity2>("Config2")
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
|
let storesFound: Void? = try? from.applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
XCTAssertTrue(storesFound)
|
XCTAssertNotNil(storesFound)
|
||||||
XCTAssertNotNil(request.entity)
|
XCTAssertNotNil(request.entity)
|
||||||
XCTAssertNotNil(request.affectedStores)
|
XCTAssertNotNil(request.safeAffectedStores())
|
||||||
|
|
||||||
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
|
||||||
|
|
||||||
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
|
let affectedConfigurations = request.safeAffectedStores()?.map { $0.configurationName } ?? []
|
||||||
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
XCTAssertEqual(affectedConfigurations, ["Config2"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// GroupByTests.swift
|
// GroupByTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -38,14 +39,14 @@ final class GroupByTests: BaseTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let groupBy = GroupBy()
|
let groupBy = GroupBy<NSManagedObject>()
|
||||||
XCTAssertEqual(groupBy, GroupBy([] as [String]))
|
XCTAssertEqual(groupBy, GroupBy([] as [String]))
|
||||||
XCTAssertNotEqual(groupBy, GroupBy("key"))
|
XCTAssertNotEqual(groupBy, GroupBy("key"))
|
||||||
XCTAssertTrue(groupBy.keyPaths.isEmpty)
|
XCTAssertTrue(groupBy.keyPaths.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let groupBy = GroupBy("key1")
|
let groupBy = GroupBy<NSManagedObject>("key1")
|
||||||
XCTAssertEqual(groupBy, GroupBy("key1"))
|
XCTAssertEqual(groupBy, GroupBy("key1"))
|
||||||
XCTAssertEqual(groupBy, GroupBy(["key1"]))
|
XCTAssertEqual(groupBy, GroupBy(["key1"]))
|
||||||
XCTAssertNotEqual(groupBy, GroupBy("key2"))
|
XCTAssertNotEqual(groupBy, GroupBy("key2"))
|
||||||
@@ -53,7 +54,7 @@ final class GroupByTests: BaseTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let groupBy = GroupBy("key1", "key2")
|
let groupBy = GroupBy<NSManagedObject>("key1", "key2")
|
||||||
XCTAssertEqual(groupBy, GroupBy("key1", "key2"))
|
XCTAssertEqual(groupBy, GroupBy("key1", "key2"))
|
||||||
XCTAssertEqual(groupBy, GroupBy(["key1", "key2"]))
|
XCTAssertEqual(groupBy, GroupBy(["key1", "key2"]))
|
||||||
XCTAssertNotEqual(groupBy, GroupBy("key2", "key1"))
|
XCTAssertNotEqual(groupBy, GroupBy("key2", "key1"))
|
||||||
@@ -66,10 +67,10 @@ final class GroupByTests: BaseTestCase {
|
|||||||
|
|
||||||
self.prepareStack { (dataStack) in
|
self.prepareStack { (dataStack) in
|
||||||
|
|
||||||
let groupBy = GroupBy("testString")
|
let groupBy = GroupBy<NSManagedObject>(#keyPath(TestEntity1.testString))
|
||||||
|
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
_ = From(TestEntity1).applyToFetchRequest(request, context: dataStack.mainContext)
|
try From<TestEntity1>().applyToFetchRequest(request, context: dataStack.mainContext)
|
||||||
groupBy.applyToFetchRequest(request)
|
groupBy.applyToFetchRequest(request)
|
||||||
|
|
||||||
XCTAssertNotNil(request.propertiesToGroupBy)
|
XCTAssertNotNil(request.propertiesToGroupBy)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>BNDL</string>
|
<string>BNDL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>6.2.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// IntoTests.swift
|
// IntoTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -36,7 +37,7 @@ final class IntoTests: XCTestCase {
|
|||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatIntoClauseConstants_AreCorrect() {
|
dynamic func test_ThatIntoClauseConstants_AreCorrect() {
|
||||||
|
|
||||||
XCTAssertEqual(Into<NSManagedObject>.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME")
|
XCTAssertEqual(DataStack.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -44,7 +45,7 @@ final class IntoTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into()
|
let into = Into<NSManagedObject>()
|
||||||
XCTAssert(into.entityClass === NSManagedObject.self)
|
XCTAssert(into.entityClass === NSManagedObject.self)
|
||||||
XCTAssertNil(into.configuration)
|
XCTAssertNil(into.configuration)
|
||||||
XCTAssertTrue(into.inferStoreIfPossible)
|
XCTAssertTrue(into.inferStoreIfPossible)
|
||||||
@@ -58,14 +59,7 @@ final class IntoTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into(TestEntity1)
|
let into = Into(TestEntity1.self)
|
||||||
XCTAssert(into.entityClass === TestEntity1.self)
|
|
||||||
XCTAssertNil(into.configuration)
|
|
||||||
XCTAssertTrue(into.inferStoreIfPossible)
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
|
|
||||||
let into = Into(TestEntity1.self as AnyClass)
|
|
||||||
XCTAssert(into.entityClass === TestEntity1.self)
|
XCTAssert(into.entityClass === TestEntity1.self)
|
||||||
XCTAssertNil(into.configuration)
|
XCTAssertNil(into.configuration)
|
||||||
XCTAssertTrue(into.inferStoreIfPossible)
|
XCTAssertTrue(into.inferStoreIfPossible)
|
||||||
@@ -84,13 +78,6 @@ final class IntoTests: XCTestCase {
|
|||||||
XCTAssertEqual(into.configuration, "Config1")
|
XCTAssertEqual(into.configuration, "Config1")
|
||||||
XCTAssertFalse(into.inferStoreIfPossible)
|
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
|
@objc
|
||||||
@@ -98,43 +85,30 @@ final class IntoTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into()
|
let into = Into<NSManagedObject>()
|
||||||
XCTAssertEqual(into, Into())
|
|
||||||
XCTAssertEqual(into, Into<NSManagedObject>())
|
XCTAssertEqual(into, Into<NSManagedObject>())
|
||||||
XCTAssertEqual(into, Into(NSManagedObject.self as AnyClass))
|
XCTAssertEqual(into, Into(NSManagedObject.self))
|
||||||
XCTAssertFalse(into == Into<TestEntity1>())
|
XCTAssertNotEqual(into, Into<NSManagedObject>(TestEntity1.self))
|
||||||
XCTAssertNotEqual(into, Into<NSManagedObject>("Config1"))
|
XCTAssertNotEqual(into, Into<NSManagedObject>("Config1"))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into<TestEntity1>()
|
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)
|
|
||||||
XCTAssertEqual(into, Into<TestEntity1>())
|
XCTAssertEqual(into, Into<TestEntity1>())
|
||||||
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass))
|
XCTAssertEqual(into, Into(TestEntity1.self))
|
||||||
XCTAssertFalse(into == Into<TestEntity2>())
|
|
||||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
|
XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into(TestEntity1.self as AnyClass)
|
let into = Into(TestEntity1.self)
|
||||||
XCTAssert(into == Into<TestEntity1>())
|
XCTAssert(into == Into<TestEntity1>())
|
||||||
XCTAssertEqual(into, Into(TestEntity1))
|
XCTAssertEqual(into, Into(TestEntity1.self))
|
||||||
XCTAssertFalse(into == Into<TestEntity2>())
|
|
||||||
XCTAssertFalse(into == Into<TestEntity1>("Config1"))
|
XCTAssertFalse(into == Into<TestEntity1>("Config1"))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into<TestEntity1>("Config1")
|
let into = Into<TestEntity1>("Config1")
|
||||||
XCTAssertEqual(into, Into(TestEntity1.self, "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"))
|
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@@ -142,16 +116,14 @@ final class IntoTests: XCTestCase {
|
|||||||
let into = Into(TestEntity1.self, "Config1")
|
let into = Into(TestEntity1.self, "Config1")
|
||||||
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
||||||
XCTAssertEqual(into, Into<TestEntity1>("Config1"))
|
XCTAssertEqual(into, Into<TestEntity1>("Config1"))
|
||||||
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
|
|
||||||
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into(TestEntity1.self as AnyClass, "Config1")
|
let into = Into(TestEntity1.self, "Config1")
|
||||||
XCTAssert(into == Into<TestEntity1>("Config1"))
|
XCTAssertEqual(into, Into<TestEntity1>("Config1"))
|
||||||
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
|
||||||
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
|
XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
|
||||||
XCTAssertFalse(into == Into<TestEntity1>("Config2"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,45 +132,9 @@ final class IntoTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let into = Into()
|
let into = Into<NSManagedObject>()
|
||||||
let objcInto = into.bridgeToObjectiveC
|
let objcInto = into.bridgeToObjectiveC
|
||||||
XCTAssertEqual(into, objcInto.bridgeToSwift)
|
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
|
// ListObserverTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,14 +23,13 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
|
||||||
|
|
||||||
// MARK: - ListObserverTests
|
// MARK: - ListObserverTests
|
||||||
|
|
||||||
class ListObserverTests: BaseTestDataTestCase {
|
class ListObserverTests: BaseTestDataTestCase {
|
||||||
@@ -42,9 +41,9 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
let observer = TestListObserver()
|
let observer = TestListObserver()
|
||||||
let monitor = stack.monitorSectionedList(
|
let monitor = stack.monitorSectionedList(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
SectionBy("testBoolean"),
|
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||||
OrderBy(.Ascending("testBoolean"), .Ascending("testEntityID"))
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||||
)
|
)
|
||||||
monitor.addObserver(observer)
|
monitor.addObserver(observer)
|
||||||
|
|
||||||
@@ -54,13 +53,13 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let willChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorWillChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -68,16 +67,16 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didInsertSectionExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitor:didInsertSection:toSectionIndex:",
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 1)
|
XCTAssertEqual(events, 1)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
(note.userInfo ?? [:]),
|
((note.userInfo as NSDictionary?) ?? [:]),
|
||||||
[
|
[
|
||||||
"sectionInfo": monitor.sectionInfoAtIndex(0),
|
"sectionInfo": monitor.sectionInfo(at: 0),
|
||||||
"sectionIndex": 0
|
"sectionIndex": 0
|
||||||
] as NSDictionary
|
] as NSDictionary
|
||||||
)
|
)
|
||||||
@@ -88,8 +87,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 1
|
return events == 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didInsertObjectExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitor:didInsertObject:toIndexPath:",
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
@@ -98,21 +97,21 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
let userInfo = note.userInfo
|
let userInfo = note.userInfo
|
||||||
XCTAssertNotNil(userInfo)
|
XCTAssertNotNil(userInfo)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
|
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||||
["indexPath", "object"]
|
["indexPath", "object"]
|
||||||
)
|
)
|
||||||
|
|
||||||
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
let indexPath = userInfo?["indexPath"] as? NSIndexPath
|
||||||
XCTAssertEqual(indexPath?.section, 0)
|
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
|
||||||
XCTAssertEqual(indexPath?.row, 0)
|
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
|
||||||
|
|
||||||
let object = userInfo?["object"] as? TestEntity1
|
let object = userInfo?["object"] as? TestEntity1
|
||||||
XCTAssertEqual(object?.testBoolean, NSNumber(bool: true))
|
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||||
XCTAssertEqual(object?.testNumber, NSNumber(integer: 1))
|
XCTAssertEqual(object?.testNumber, NSNumber(value: 1))
|
||||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "1"))
|
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "1"))
|
||||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:1")
|
XCTAssertEqual(object?.testString, "nil:TestEntity1:1")
|
||||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:1" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
|
XCTAssertEqual(object?.testData, ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!)
|
||||||
XCTAssertEqual(object?.testDate, self.dateFormatter.dateFromString("2000-01-01T00:00:00Z")!)
|
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!)
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -120,12 +119,12 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 2
|
return events == 2
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorDidChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -133,30 +132,30 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 3
|
return events == 3
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
let object = transaction.create(Into(TestEntity1))
|
|
||||||
object.testBoolean = NSNumber(bool: true)
|
|
||||||
object.testNumber = NSNumber(integer: 1)
|
|
||||||
object.testDecimal = NSDecimalNumber(string: "1")
|
|
||||||
object.testString = "nil:TestEntity1:1"
|
|
||||||
object.testData = ("nil:TestEntity1:1" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
|
|
||||||
object.testDate = self.dateFormatter.dateFromString("2000-01-01T00:00:00Z")!
|
|
||||||
|
|
||||||
transaction.commit { (result) in
|
|
||||||
|
|
||||||
switch result {
|
let object = transaction.create(Into<TestEntity1>())
|
||||||
|
object.testBoolean = NSNumber(value: true)
|
||||||
case .Success(let hasChanges):
|
object.testNumber = NSNumber(value: 1)
|
||||||
XCTAssertTrue(hasChanges)
|
object.testDecimal = NSDecimalNumber(string: "1")
|
||||||
saveExpectation.fulfill()
|
object.testString = "nil:TestEntity1:1"
|
||||||
|
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||||
case .Failure:
|
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||||
XCTFail()
|
|
||||||
}
|
return transaction.hasChanges
|
||||||
|
},
|
||||||
|
success: { (hasChanges) in
|
||||||
|
|
||||||
|
XCTAssertTrue(hasChanges)
|
||||||
|
saveExpectation.fulfill()
|
||||||
|
},
|
||||||
|
failure: { _ in
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
self.waitAndCheckExpectations()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,28 +169,28 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
let observer = TestListObserver()
|
let observer = TestListObserver()
|
||||||
let monitor = stack.monitorSectionedList(
|
let monitor = stack.monitorSectionedList(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
SectionBy("testBoolean"),
|
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||||
OrderBy(.Ascending("testBoolean"), .Ascending("testEntityID"))
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||||
)
|
)
|
||||||
monitor.addObserver(observer)
|
monitor.addObserver(observer)
|
||||||
|
|
||||||
XCTAssertTrue(monitor.hasSections())
|
XCTAssertTrue(monitor.hasSections())
|
||||||
XCTAssertEqual(monitor.numberOfSections(), 2)
|
XCTAssertEqual(monitor.numberOfSections(), 2)
|
||||||
XCTAssertTrue(monitor.hasObjects())
|
XCTAssertTrue(monitor.hasObjects())
|
||||||
XCTAssertTrue(monitor.hasObjectsInSection(0))
|
XCTAssertTrue(monitor.hasObjects(in: 0))
|
||||||
XCTAssertEqual(monitor.numberOfObjectsInSection(0), 2)
|
XCTAssertEqual(monitor.numberOfObjects(in: 0), 2)
|
||||||
XCTAssertEqual(monitor.numberOfObjectsInSection(1), 3)
|
XCTAssertEqual(monitor.numberOfObjects(in: 1), 3)
|
||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let willChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorWillChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -199,67 +198,65 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
for _ in 1 ... 2 {
|
|
||||||
|
_ = self.expectation(
|
||||||
let didUpdateObjectExpectation = self.expectationForNotification(
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"),
|
||||||
"listMonitor:didUpdateObject:atIndexPath:",
|
object: observer,
|
||||||
object: observer,
|
handler: { (note) -> Bool in
|
||||||
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
|
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||||
XCTAssertNotNil(userInfo)
|
XCTAssertEqual(object?.testNumber, NSNumber(value: 11))
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "11"))
|
||||||
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
|
XCTAssertEqual(object?.testString, "nil:TestEntity1:11")
|
||||||
["indexPath", "object"]
|
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
|
case NSNumber(value: 102)?:
|
||||||
let object = userInfo?["object"] as? TestEntity1
|
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
|
||||||
|
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
|
||||||
|
|
||||||
switch object?.testEntityID {
|
XCTAssertEqual(object?.testBoolean, NSNumber(value: false))
|
||||||
|
XCTAssertEqual(object?.testNumber, NSNumber(value: 22))
|
||||||
case NSNumber(integer: 101)?:
|
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "22"))
|
||||||
XCTAssertEqual(indexPath?.section, 1)
|
XCTAssertEqual(object?.testString, "nil:TestEntity1:22")
|
||||||
XCTAssertEqual(indexPath?.row, 0)
|
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")!)
|
||||||
XCTAssertEqual(object?.testBoolean, NSNumber(bool: true))
|
|
||||||
XCTAssertEqual(object?.testNumber, NSNumber(integer: 11))
|
default:
|
||||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "11"))
|
XCTFail()
|
||||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:11")
|
|
||||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:11" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
|
|
||||||
XCTAssertEqual(object?.testDate, self.dateFormatter.dateFromString("2000-01-11T00:00:00Z")!)
|
|
||||||
|
|
||||||
case NSNumber(integer: 102)?:
|
|
||||||
XCTAssertEqual(indexPath?.section, 0)
|
|
||||||
XCTAssertEqual(indexPath?.row, 0)
|
|
||||||
|
|
||||||
XCTAssertEqual(object?.testBoolean, NSNumber(bool: false))
|
|
||||||
XCTAssertEqual(object?.testNumber, NSNumber(integer: 22))
|
|
||||||
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "22"))
|
|
||||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:22")
|
|
||||||
XCTAssertEqual(object?.testData, ("nil:TestEntity1:22" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
|
|
||||||
XCTAssertEqual(object?.testDate, self.dateFormatter.dateFromString("2000-01-22T00:00:00Z")!)
|
|
||||||
|
|
||||||
default:
|
|
||||||
XCTFail()
|
|
||||||
}
|
|
||||||
defer {
|
|
||||||
|
|
||||||
events += 1
|
|
||||||
}
|
|
||||||
return events == 1 || events == 2
|
|
||||||
}
|
}
|
||||||
)
|
defer {
|
||||||
}
|
|
||||||
let didChangeExpectation = self.expectationForNotification(
|
events += 1
|
||||||
"listMonitorDidChange:",
|
}
|
||||||
|
return events == 1 || events == 2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
_ = self.expectation(
|
||||||
|
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 3)
|
XCTAssertEqual(events, 3)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -267,50 +264,50 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 3
|
return events == 3
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
if let object = transaction.fetchOne(
|
|
||||||
From(TestEntity1),
|
|
||||||
Where("testEntityID", isEqualTo: 101)) {
|
|
||||||
|
|
||||||
object.testNumber = NSNumber(integer: 11)
|
if let object = try transaction.fetchOne(
|
||||||
object.testDecimal = NSDecimalNumber(string: "11")
|
From<TestEntity1>(),
|
||||||
object.testString = "nil:TestEntity1:11"
|
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
|
||||||
object.testData = ("nil:TestEntity1:11" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
|
|
||||||
object.testDate = self.dateFormatter.dateFromString("2000-01-11T00:00:00Z")!
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
XCTFail()
|
|
||||||
}
|
|
||||||
if let object = transaction.fetchOne(
|
|
||||||
From(TestEntity1),
|
|
||||||
Where("testEntityID", isEqualTo: 102)) {
|
|
||||||
|
|
||||||
object.testNumber = NSNumber(integer: 22)
|
|
||||||
object.testDecimal = NSDecimalNumber(string: "22")
|
|
||||||
object.testString = "nil:TestEntity1:22"
|
|
||||||
object.testData = ("nil:TestEntity1:22" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
|
|
||||||
object.testDate = self.dateFormatter.dateFromString("2000-01-22T00:00:00Z")!
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
XCTFail()
|
|
||||||
}
|
|
||||||
transaction.commit { (result) in
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
|
|
||||||
case .Success(let hasChanges):
|
object.testNumber = NSNumber(value: 11)
|
||||||
XCTAssertTrue(hasChanges)
|
object.testDecimal = NSDecimalNumber(string: "11")
|
||||||
saveExpectation.fulfill()
|
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()
|
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()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,21 +321,21 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
let observer = TestListObserver()
|
let observer = TestListObserver()
|
||||||
let monitor = stack.monitorSectionedList(
|
let monitor = stack.monitorSectionedList(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
SectionBy("testBoolean"),
|
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||||
OrderBy(.Ascending("testBoolean"), .Ascending("testEntityID"))
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||||
)
|
)
|
||||||
monitor.addObserver(observer)
|
monitor.addObserver(observer)
|
||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let willChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorWillChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -346,8 +343,8 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didMoveObjectExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitor:didMoveObject:fromIndexPath:toIndexPath:",
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
@@ -356,21 +353,21 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
let userInfo = note.userInfo
|
let userInfo = note.userInfo
|
||||||
XCTAssertNotNil(userInfo)
|
XCTAssertNotNil(userInfo)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
|
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||||
["fromIndexPath", "toIndexPath", "object"]
|
["fromIndexPath", "toIndexPath", "object"]
|
||||||
)
|
)
|
||||||
|
|
||||||
let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath
|
let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath
|
||||||
XCTAssertEqual(fromIndexPath?.section, 0)
|
XCTAssertEqual(fromIndexPath?.index(atPosition: 0), 0)
|
||||||
XCTAssertEqual(fromIndexPath?.row, 0)
|
XCTAssertEqual(fromIndexPath?.index(atPosition: 1), 0)
|
||||||
|
|
||||||
let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath
|
let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath
|
||||||
XCTAssertEqual(toIndexPath?.section, 1)
|
XCTAssertEqual(toIndexPath?.index(atPosition: 0), 1)
|
||||||
XCTAssertEqual(toIndexPath?.row, 1)
|
XCTAssertEqual(toIndexPath?.index(atPosition: 1), 1)
|
||||||
|
|
||||||
let object = userInfo?["object"] as? TestEntity1
|
let object = userInfo?["object"] as? TestEntity1
|
||||||
XCTAssertEqual(object?.testEntityID, NSNumber(integer: 102))
|
XCTAssertEqual(object?.testEntityID, NSNumber(value: 102))
|
||||||
XCTAssertEqual(object?.testBoolean, NSNumber(bool: true))
|
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
@@ -379,13 +376,13 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 1
|
return events == 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorDidChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 2)
|
XCTAssertEqual(events, 2)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -393,32 +390,32 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 2
|
return events == 2
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
if let object = transaction.fetchOne(
|
|
||||||
From(TestEntity1),
|
|
||||||
Where("testEntityID", isEqualTo: 102)) {
|
|
||||||
|
|
||||||
object.testBoolean = NSNumber(bool: true)
|
if let object = try transaction.fetchOne(
|
||||||
}
|
From<TestEntity1>(),
|
||||||
else {
|
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()
|
XCTFail()
|
||||||
}
|
}
|
||||||
transaction.commit { (result) in
|
)
|
||||||
|
|
||||||
switch result {
|
|
||||||
|
|
||||||
case .Success(let hasChanges):
|
|
||||||
XCTAssertTrue(hasChanges)
|
|
||||||
saveExpectation.fulfill()
|
|
||||||
|
|
||||||
case .Failure:
|
|
||||||
XCTFail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.waitAndCheckExpectations()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,21 +429,21 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
let observer = TestListObserver()
|
let observer = TestListObserver()
|
||||||
let monitor = stack.monitorSectionedList(
|
let monitor = stack.monitorSectionedList(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
SectionBy("testBoolean"),
|
SectionBy(#keyPath(TestEntity1.testBoolean)),
|
||||||
OrderBy(.Ascending("testBoolean"), .Ascending("testEntityID"))
|
OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
|
||||||
)
|
)
|
||||||
monitor.addObserver(observer)
|
monitor.addObserver(observer)
|
||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let willChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorWillChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -454,40 +451,37 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
for _ in 1 ... 2 {
|
_ = self.expectation(
|
||||||
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"),
|
||||||
let didUpdateObjectExpectation = self.expectationForNotification(
|
object: observer,
|
||||||
"listMonitor:didDeleteObject:fromIndexPath:",
|
handler: { (note) -> Bool in
|
||||||
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)
|
events += 1
|
||||||
|
|
||||||
let userInfo = note.userInfo
|
|
||||||
XCTAssertNotNil(userInfo)
|
|
||||||
XCTAssertEqual(
|
|
||||||
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
|
|
||||||
["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?.deleted, true)
|
|
||||||
|
|
||||||
defer {
|
|
||||||
|
|
||||||
events += 1
|
|
||||||
}
|
|
||||||
return events == 1 || events == 2
|
|
||||||
}
|
}
|
||||||
)
|
return events == 1 || events == 2
|
||||||
}
|
}
|
||||||
let didDeleteSectionExpectation = self.expectationForNotification(
|
)
|
||||||
"listMonitor:didDeleteSection:fromSectionIndex:",
|
_ = self.expectation(
|
||||||
|
forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
@@ -496,16 +490,16 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
let userInfo = note.userInfo
|
let userInfo = note.userInfo
|
||||||
XCTAssertNotNil(userInfo)
|
XCTAssertNotNil(userInfo)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
|
Set(userInfo?.keys.map({ $0 as! String }) ?? []),
|
||||||
["sectionInfo", "sectionIndex"]
|
["sectionInfo", "sectionIndex"]
|
||||||
)
|
)
|
||||||
|
|
||||||
let sectionInfo = userInfo?["sectionInfo"]
|
let sectionInfo = userInfo?["sectionInfo"] as? NSFetchedResultsSectionInfo
|
||||||
XCTAssertNotNil(sectionInfo)
|
XCTAssertNotNil(sectionInfo)
|
||||||
XCTAssertEqual(sectionInfo?.name, "0")
|
XCTAssertEqual(sectionInfo?.name, "0")
|
||||||
|
|
||||||
let sectionIndex = userInfo?["sectionIndex"]
|
let sectionIndex = userInfo?["sectionIndex"]
|
||||||
XCTAssertEqual(sectionIndex as? NSNumber, NSNumber(integer: 0))
|
XCTAssertEqual(sectionIndex as? NSNumber, NSNumber(value: 0))
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
@@ -514,13 +508,13 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 3
|
return events == 3
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didChangeExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"listMonitorDidChange:",
|
forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 4)
|
XCTAssertEqual(events, 4)
|
||||||
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
|
XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary())
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
events += 1
|
events += 1
|
||||||
@@ -528,26 +522,27 @@ class ListObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 4
|
return events == 4
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
transaction.deleteAll(
|
|
||||||
From(TestEntity1),
|
|
||||||
Where("testBoolean", isEqualTo: false)
|
|
||||||
)
|
|
||||||
transaction.commit { (result) in
|
|
||||||
|
|
||||||
switch result {
|
let count = try transaction.deleteAll(
|
||||||
|
From<TestEntity1>(),
|
||||||
case .Success(let hasChanges):
|
Where<TestEntity1>(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
|
||||||
XCTAssertTrue(hasChanges)
|
)
|
||||||
saveExpectation.fulfill()
|
XCTAssertEqual(count, 2)
|
||||||
|
return transaction.hasChanges
|
||||||
case .Failure:
|
},
|
||||||
XCTFail()
|
success: { (hasChanges) in
|
||||||
}
|
|
||||||
|
XCTAssertTrue(hasChanges)
|
||||||
|
saveExpectation.fulfill()
|
||||||
|
},
|
||||||
|
failure: { _ in
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
self.waitAndCheckExpectations()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -562,37 +557,37 @@ class TestListObserver: ListSectionObserver {
|
|||||||
|
|
||||||
typealias ListEntityType = TestEntity1
|
typealias ListEntityType = TestEntity1
|
||||||
|
|
||||||
func listMonitorWillChange(monitor: ListMonitor<TestEntity1>) {
|
func listMonitorWillChange(_ monitor: ListMonitor<TestEntity1>) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitorWillChange:",
|
name: Notification.Name(rawValue: "listMonitorWillChange:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [:]
|
userInfo: [:]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitorDidChange(monitor: ListMonitor<TestEntity1>) {
|
func listMonitorDidChange(_ monitor: ListMonitor<TestEntity1>) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitorDidChange:",
|
name: Notification.Name(rawValue: "listMonitorDidChange:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [:]
|
userInfo: [:]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitorWillRefetch(monitor: ListMonitor<TestEntity1>) {
|
func listMonitorWillRefetch(_ monitor: ListMonitor<TestEntity1>) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitorWillRefetch:",
|
name: Notification.Name(rawValue: "listMonitorWillRefetch:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [:]
|
userInfo: [:]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitorDidRefetch(monitor: ListMonitor<TestEntity1>) {
|
func listMonitorDidRefetch(_ monitor: ListMonitor<TestEntity1>) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitorDidRefetch:",
|
name: Notification.Name(rawValue: "listMonitorDidRefetch:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [:]
|
userInfo: [:]
|
||||||
)
|
)
|
||||||
@@ -601,10 +596,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
|
|
||||||
// MARK: ListObjectObserver
|
// MARK: ListObjectObserver
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didInsertObject object: TestEntity1, toIndexPath indexPath: NSIndexPath) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didInsertObject object: TestEntity1, toIndexPath indexPath: IndexPath) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didInsertObject:toIndexPath:",
|
name: Notification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object,
|
"object": object,
|
||||||
@@ -613,10 +608,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didDeleteObject object: TestEntity1, fromIndexPath indexPath: NSIndexPath) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didDeleteObject object: TestEntity1, fromIndexPath indexPath: IndexPath) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didDeleteObject:fromIndexPath:",
|
name: Notification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object,
|
"object": object,
|
||||||
@@ -625,10 +620,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didUpdateObject object: TestEntity1, atIndexPath indexPath: NSIndexPath) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didUpdateObject object: TestEntity1, atIndexPath indexPath: IndexPath) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didUpdateObject:atIndexPath:",
|
name: Notification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object,
|
"object": object,
|
||||||
@@ -638,10 +633,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didMoveObject object: TestEntity1, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didMoveObject object: TestEntity1, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didMoveObject:fromIndexPath:toIndexPath:",
|
name: Notification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object,
|
"object": object,
|
||||||
@@ -654,10 +649,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
|
|
||||||
// MARK: ListSectionObserver
|
// MARK: ListSectionObserver
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didInsertSection:toSectionIndex:",
|
name: Notification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"sectionInfo": sectionInfo,
|
"sectionInfo": sectionInfo,
|
||||||
@@ -666,10 +661,10 @@ class TestListObserver: ListSectionObserver {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMonitor(monitor: ListMonitor<TestEntity1>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
func listMonitor(_ monitor: ListMonitor<TestEntity1>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"listMonitor:didDeleteSection:fromSectionIndex:",
|
name: Notification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"sectionInfo": sectionInfo,
|
"sectionInfo": sectionInfo,
|
||||||
@@ -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
|
// MigrationChainTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -37,8 +37,8 @@ final class MigrationChainTests: XCTestCase {
|
|||||||
dynamic func test_ThatNilMigrationChains_HaveNoVersions() {
|
dynamic func test_ThatNilMigrationChains_HaveNoVersions() {
|
||||||
|
|
||||||
let chain: MigrationChain = nil
|
let chain: MigrationChain = nil
|
||||||
XCTAssertTrue(chain.valid)
|
XCTAssertTrue(chain.isValid)
|
||||||
XCTAssertTrue(chain.empty)
|
XCTAssertTrue(chain.isEmpty)
|
||||||
|
|
||||||
XCTAssertFalse(chain.contains("version1"))
|
XCTAssertFalse(chain.contains("version1"))
|
||||||
XCTAssertNil(chain.nextVersionFrom("version1"))
|
XCTAssertNil(chain.nextVersionFrom("version1"))
|
||||||
@@ -48,8 +48,8 @@ final class MigrationChainTests: XCTestCase {
|
|||||||
dynamic func test_ThatStringMigrationChains_HaveOneVersion() {
|
dynamic func test_ThatStringMigrationChains_HaveOneVersion() {
|
||||||
|
|
||||||
let chain: MigrationChain = "version1"
|
let chain: MigrationChain = "version1"
|
||||||
XCTAssertTrue(chain.valid)
|
XCTAssertTrue(chain.isValid)
|
||||||
XCTAssertTrue(chain.empty)
|
XCTAssertTrue(chain.isEmpty)
|
||||||
|
|
||||||
XCTAssertTrue(chain.contains("version1"))
|
XCTAssertTrue(chain.contains("version1"))
|
||||||
XCTAssertFalse(chain.contains("version2"))
|
XCTAssertFalse(chain.contains("version2"))
|
||||||
@@ -62,8 +62,8 @@ final class MigrationChainTests: XCTestCase {
|
|||||||
dynamic func test_ThatArrayMigrationChains_HaveLinearVersions() {
|
dynamic func test_ThatArrayMigrationChains_HaveLinearVersions() {
|
||||||
|
|
||||||
let chain: MigrationChain = ["version1", "version2", "version3", "version4"]
|
let chain: MigrationChain = ["version1", "version2", "version3", "version4"]
|
||||||
XCTAssertTrue(chain.valid)
|
XCTAssertTrue(chain.isValid)
|
||||||
XCTAssertFalse(chain.empty)
|
XCTAssertFalse(chain.isEmpty)
|
||||||
|
|
||||||
XCTAssertTrue(chain.contains("version1"))
|
XCTAssertTrue(chain.contains("version1"))
|
||||||
XCTAssertTrue(chain.contains("version2"))
|
XCTAssertTrue(chain.contains("version2"))
|
||||||
@@ -86,8 +86,8 @@ final class MigrationChainTests: XCTestCase {
|
|||||||
"version2": "version3",
|
"version2": "version3",
|
||||||
"version3": "version4"
|
"version3": "version4"
|
||||||
]
|
]
|
||||||
XCTAssertTrue(chain.valid)
|
XCTAssertTrue(chain.isValid)
|
||||||
XCTAssertFalse(chain.empty)
|
XCTAssertFalse(chain.isEmpty)
|
||||||
|
|
||||||
XCTAssertTrue(chain.contains("version1"))
|
XCTAssertTrue(chain.contains("version1"))
|
||||||
XCTAssertTrue(chain.contains("version2"))
|
XCTAssertTrue(chain.contains("version2"))
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?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">
|
<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="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="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="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"/>
|
<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>
|
||||||
<entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES">
|
<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="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="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="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"/>
|
<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>
|
</entity>
|
||||||
<configuration name="Config1">
|
<configuration name="Config1">
|
||||||
<memberEntity name="TestEntity1AAA"/>
|
<memberEntity name="TestEntity1AAA"/>
|
||||||
@@ -27,7 +31,7 @@
|
|||||||
<memberEntity name="TestEntity2"/>
|
<memberEntity name="TestEntity2"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="TestEntity1AAA" positionX="-63" positionY="-18" 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="165"/>
|
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="195"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// ObjectObserverTests.swift
|
// ObjectObserverTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,8 +29,6 @@ import XCTest
|
|||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
|
||||||
|
|
||||||
// MARK: - ObjectObserverTests
|
// MARK: - ObjectObserverTests
|
||||||
|
|
||||||
class ObjectObserverTests: BaseTestDataTestCase {
|
class ObjectObserverTests: BaseTestDataTestCase {
|
||||||
@@ -42,9 +40,9 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
self.prepareTestDataForStack(stack)
|
self.prepareTestDataForStack(stack)
|
||||||
|
|
||||||
guard let object = stack.fetchOne(
|
guard let object = try stack.fetchOne(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
Where("testEntityID", isEqualTo: 101)) else {
|
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||||
|
|
||||||
XCTFail()
|
XCTFail()
|
||||||
return
|
return
|
||||||
@@ -58,14 +56,14 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let willUpdateExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"objectMonitor:willUpdateObject:",
|
forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
(note.userInfo ?? [:]),
|
((note.userInfo as NSDictionary?) ?? [:]),
|
||||||
["object": object] as NSDictionary
|
["object": object] as NSDictionary
|
||||||
)
|
)
|
||||||
defer {
|
defer {
|
||||||
@@ -75,26 +73,26 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let didUpdateExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"objectMonitor:didUpdateObject:changedPersistentKeys:",
|
forNotification: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 1)
|
XCTAssertEqual(events, 1)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
(note.userInfo ?? [:]),
|
((note.userInfo as NSDictionary?) ?? [:]),
|
||||||
[
|
[
|
||||||
"object": object,
|
"object": object,
|
||||||
"changedPersistentKeys": Set(
|
"changedPersistentKeys": Set(
|
||||||
[
|
[
|
||||||
"testNumber",
|
#keyPath(TestEntity1.testNumber),
|
||||||
"testString"
|
#keyPath(TestEntity1.testString)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
] as NSDictionary
|
] as NSDictionary
|
||||||
)
|
)
|
||||||
let object = note.userInfo?["object"] as? TestEntity1
|
let object = note.userInfo?["object"] as? TestEntity1
|
||||||
XCTAssertEqual(object?.testNumber, NSNumber(integer: 10))
|
XCTAssertEqual(object?.testNumber, NSNumber(value: 10))
|
||||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:10")
|
XCTAssertEqual(object?.testString, "nil:TestEntity1:10")
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
@@ -104,30 +102,30 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 1
|
return events == 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
guard let object = transaction.edit(object) else {
|
|
||||||
|
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()
|
XCTFail()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
object.testNumber = NSNumber(integer: 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()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,9 +137,9 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
self.prepareTestDataForStack(stack)
|
self.prepareTestDataForStack(stack)
|
||||||
|
|
||||||
guard let object = stack.fetchOne(
|
guard let object = try stack.fetchOne(
|
||||||
From(TestEntity1),
|
From<TestEntity1>(),
|
||||||
Where("testEntityID", isEqualTo: 101)) else {
|
Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
|
||||||
|
|
||||||
XCTFail()
|
XCTFail()
|
||||||
return
|
return
|
||||||
@@ -155,14 +153,14 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
|
|
||||||
var events = 0
|
var events = 0
|
||||||
|
|
||||||
let didDeleteExpectation = self.expectationForNotification(
|
_ = self.expectation(
|
||||||
"objectMonitor:didDeleteObject:",
|
forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"),
|
||||||
object: observer,
|
object: observer,
|
||||||
handler: { (note) -> Bool in
|
handler: { (note) -> Bool in
|
||||||
|
|
||||||
XCTAssertEqual(events, 0)
|
XCTAssertEqual(events, 0)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
(note.userInfo ?? [:]),
|
((note.userInfo as NSDictionary?) ?? [:]),
|
||||||
["object": object] as NSDictionary
|
["object": object] as NSDictionary
|
||||||
)
|
)
|
||||||
defer {
|
defer {
|
||||||
@@ -172,30 +170,30 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
|||||||
return events == 0
|
return events == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let saveExpectation = self.expectationWithDescription("save")
|
let saveExpectation = self.expectation(description: "save")
|
||||||
stack.beginAsynchronous { (transaction) in
|
stack.perform(
|
||||||
|
asynchronous: { (transaction) -> Bool in
|
||||||
guard let object = transaction.edit(object) else {
|
|
||||||
|
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()
|
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()
|
self.waitAndCheckExpectations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,10 +206,10 @@ class TestObjectObserver: ObjectObserver {
|
|||||||
|
|
||||||
typealias ObjectEntityType = TestEntity1
|
typealias ObjectEntityType = TestEntity1
|
||||||
|
|
||||||
func objectMonitor(monitor: ObjectMonitor<TestEntity1>, willUpdateObject object: TestEntity1) {
|
func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, willUpdateObject object: TestEntity1) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"objectMonitor:willUpdateObject:",
|
name: Notification.Name(rawValue: "objectMonitor:willUpdateObject:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object
|
"object": object
|
||||||
@@ -219,10 +217,10 @@ 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>) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"objectMonitor:didUpdateObject:changedPersistentKeys:",
|
name: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object,
|
"object": object,
|
||||||
@@ -231,10 +229,10 @@ class TestObjectObserver: ObjectObserver {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectMonitor(monitor: ObjectMonitor<TestEntity1>, didDeleteObject object: TestEntity1) {
|
func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, didDeleteObject object: TestEntity1) {
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
NotificationCenter.default.post(
|
||||||
"objectMonitor:didDeleteObject:",
|
name: Notification.Name(rawValue: "objectMonitor:didDeleteObject:"),
|
||||||
object: self,
|
object: self,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
"object": object
|
"object": object
|
||||||
@@ -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
|
// OrderByTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -38,21 +39,21 @@ final class OrderByTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let orderBy = OrderBy()
|
let orderBy = OrderBy<NSManagedObject>()
|
||||||
XCTAssertEqual(orderBy, OrderBy([] as [NSSortDescriptor]))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([NSSortDescriptor]()))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key", ascending: false)))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key", ascending: false)))
|
||||||
XCTAssertTrue(orderBy.sortDescriptors.isEmpty)
|
XCTAssertTrue(orderBy.sortDescriptors.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
||||||
let orderBy = OrderBy(sortDescriptor)
|
let orderBy = OrderBy<NSManagedObject>(sortDescriptor)
|
||||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
|
XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
|
||||||
XCTAssertEqual(orderBy, OrderBy(.Ascending("key1")))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key2")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Descending("key1")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key1", ascending: false)))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key1", ascending: false)))
|
||||||
XCTAssertEqual(orderBy, OrderBy([sortDescriptor]))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
|
||||||
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@@ -61,76 +62,76 @@ final class OrderByTests: XCTestCase {
|
|||||||
NSSortDescriptor(key: "key1", ascending: true),
|
NSSortDescriptor(key: "key1", ascending: true),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
let orderBy = OrderBy(sortDescriptors)
|
let orderBy = OrderBy<NSManagedObject>(sortDescriptors)
|
||||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||||
XCTAssertEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key2")))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||||
XCTAssertNotEqual(
|
XCTAssertNotEqual(
|
||||||
orderBy,
|
orderBy,
|
||||||
OrderBy(
|
OrderBy<NSManagedObject>(
|
||||||
[
|
[
|
||||||
NSSortDescriptor(key: "key1", ascending: false),
|
NSSortDescriptor(key: "key1", ascending: false),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Ascending("key2")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key3")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let orderBy = OrderBy(.Ascending("key1"))
|
let orderBy = OrderBy<NSManagedObject>(.ascending("key1"))
|
||||||
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
|
||||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptor))
|
||||||
XCTAssertEqual(orderBy, OrderBy(.Ascending("key1")))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Descending("key1")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key2")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
|
||||||
XCTAssertEqual(orderBy, OrderBy([sortDescriptor]))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
|
||||||
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let orderBy = OrderBy(.Ascending("key1"), .Descending("key2"))
|
let orderBy = OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"))
|
||||||
let sortDescriptors = [
|
let sortDescriptors = [
|
||||||
NSSortDescriptor(key: "key1", ascending: true),
|
NSSortDescriptor(key: "key1", ascending: true),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||||
XCTAssertEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key2")))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||||
XCTAssertNotEqual(
|
XCTAssertNotEqual(
|
||||||
orderBy,
|
orderBy,
|
||||||
OrderBy(
|
OrderBy<NSManagedObject>(
|
||||||
[
|
[
|
||||||
NSSortDescriptor(key: "key1", ascending: false),
|
NSSortDescriptor(key: "key1", ascending: false),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Ascending("key2")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key3")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sortKeys: [SortKey] = [.Ascending("key1"), .Descending("key2")]
|
let sortKeys: [OrderBy<NSManagedObject>.SortKey] = [.ascending("key1"), .descending("key2")]
|
||||||
let orderBy = OrderBy(sortKeys)
|
let orderBy = OrderBy<NSManagedObject>(sortKeys)
|
||||||
let sortDescriptors = [
|
let sortDescriptors = [
|
||||||
NSSortDescriptor(key: "key1", ascending: true),
|
NSSortDescriptor(key: "key1", ascending: true),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
XCTAssertEqual(orderBy, OrderBy(sortDescriptors))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
|
||||||
XCTAssertEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key2")))
|
XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||||
XCTAssertNotEqual(
|
XCTAssertNotEqual(
|
||||||
orderBy,
|
orderBy,
|
||||||
OrderBy(
|
OrderBy<NSManagedObject>(
|
||||||
[
|
[
|
||||||
NSSortDescriptor(key: "key1", ascending: false),
|
NSSortDescriptor(key: "key1", ascending: false),
|
||||||
NSSortDescriptor(key: "key2", ascending: false)
|
NSSortDescriptor(key: "key2", ascending: false)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Ascending("key2")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
|
||||||
XCTAssertNotEqual(orderBy, OrderBy(.Ascending("key1"), .Descending("key3")))
|
XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
|
||||||
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,15 +139,15 @@ final class OrderByTests: XCTestCase {
|
|||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() {
|
dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() {
|
||||||
|
|
||||||
let orderBy1 = OrderBy(.Ascending("key1"))
|
let orderBy1 = OrderBy<NSManagedObject>(.ascending("key1"))
|
||||||
let orderBy2 = OrderBy(.Descending("key2"))
|
let orderBy2 = OrderBy<NSManagedObject>(.descending("key2"))
|
||||||
let orderBy3 = OrderBy(.Ascending("key3"))
|
let orderBy3 = OrderBy<NSManagedObject>(.ascending("key3"))
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let plusOrderBy = orderBy1 + orderBy2 + orderBy3
|
let plusOrderBy = orderBy1 + orderBy2 + orderBy3
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1"), .Descending("key2"), .Ascending("key3")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1")) + OrderBy(.Descending("key2"), .Ascending("key3")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2"), .ascending("key3")))
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
||||||
@@ -158,14 +159,14 @@ final class OrderByTests: XCTestCase {
|
|||||||
|
|
||||||
var plusOrderBy = orderBy1
|
var plusOrderBy = orderBy1
|
||||||
plusOrderBy += orderBy2
|
plusOrderBy += orderBy2
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1"), .Descending("key2")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1")) + OrderBy(.Descending("key2")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2")))
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1)
|
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1)
|
||||||
XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors)
|
XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors)
|
||||||
|
|
||||||
plusOrderBy += orderBy3
|
plusOrderBy += orderBy3
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1"), .Descending("key2"), .Ascending("key3")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
|
||||||
XCTAssertEqual(plusOrderBy, OrderBy(.Ascending("key1"), .Descending("key2")) + OrderBy(.Ascending("key3")))
|
XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")) + OrderBy<NSManagedObject>(.ascending("key3")))
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
|
||||||
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
|
||||||
@@ -178,8 +179,8 @@ final class OrderByTests: XCTestCase {
|
|||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() {
|
dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() {
|
||||||
|
|
||||||
let orderBy = OrderBy(.Ascending("key"))
|
let orderBy = OrderBy<NSManagedObject>(.ascending("key"))
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
orderBy.applyToFetchRequest(request)
|
orderBy.applyToFetchRequest(request)
|
||||||
XCTAssertNotNil(request.sortDescriptors)
|
XCTAssertNotNil(request.sortDescriptors)
|
||||||
XCTAssertEqual(request.sortDescriptors ?? [], orderBy.sortDescriptors)
|
XCTAssertEqual(request.sortDescriptors ?? [], orderBy.sortDescriptors)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SectionByTests.swift
|
// SectionByTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,14 +23,13 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
#if os(iOS) || os(watchOS) || os(tvOS)
|
|
||||||
|
|
||||||
//MARK: - SectionByTests
|
//MARK: - SectionByTests
|
||||||
|
|
||||||
final class SectionByTests: XCTestCase {
|
final class SectionByTests: XCTestCase {
|
||||||
@@ -40,18 +39,19 @@ final class SectionByTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sectionBy = SectionBy("key")
|
let sectionBy = SectionBy<NSManagedObject>("key")
|
||||||
XCTAssertEqual(sectionBy.sectionKeyPath, "key")
|
XCTAssertEqual(sectionBy.sectionKeyPath, "key")
|
||||||
XCTAssertEqual(sectionBy.sectionIndexTransformer(sectionName: "key"), "key")
|
XCTAssertNil(sectionBy.sectionIndexTransformer("key"))
|
||||||
}
|
}
|
||||||
do {
|
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.sectionKeyPath, "key")
|
||||||
XCTAssertEqual(sectionBy.sectionIndexTransformer(sectionName: "key"), "key:suffix")
|
XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key:suffix")
|
||||||
XCTAssertNil(sectionBy.sectionIndexTransformer(sectionName: nil))
|
XCTAssertNil(sectionBy.sectionIndexTransformer(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SelectTests.swift
|
// SelectTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -38,18 +39,18 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term: SelectTerm = "attribute"
|
let term: SelectTerm<NSManagedObject> = "attribute"
|
||||||
XCTAssertEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Attribute(let key):
|
case ._attribute(let key):
|
||||||
XCTAssertEqual(key, "attribute")
|
XCTAssertEqual(key, "attribute")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -58,17 +59,17 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Attribute("attribute")
|
let term = SelectTerm<NSManagedObject>.attribute("attribute")
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Attribute(let key):
|
case ._attribute(let key):
|
||||||
XCTAssertEqual(key, "attribute")
|
XCTAssertEqual(key, "attribute")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -82,23 +83,23 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Average("attribute")
|
let term = SelectTerm<NSManagedObject>.average("attribute")
|
||||||
XCTAssertEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute", As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "average:")
|
XCTAssertEqual(function, "average:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "average(attribute)")
|
XCTAssertEqual(alias, "average(attribute)")
|
||||||
XCTAssertTrue(nativeType == .DecimalAttributeType)
|
XCTAssertTrue(nativeType == .decimalAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -106,23 +107,23 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Average("attribute", As: "alias")
|
let term = SelectTerm<NSManagedObject>.average("attribute", as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.Average("attribute", As: "alias"))
|
XCTAssertEqual(term, SelectTerm.average("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute", As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "average:")
|
XCTAssertEqual(function, "average:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .DecimalAttributeType)
|
XCTAssertTrue(nativeType == .decimalAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -135,23 +136,23 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Count("attribute")
|
let term = SelectTerm<NSManagedObject>.count("attribute")
|
||||||
XCTAssertEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute", As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "count:")
|
XCTAssertEqual(function, "count:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "count(attribute)")
|
XCTAssertEqual(alias, "count(attribute)")
|
||||||
XCTAssertTrue(nativeType == .Integer64AttributeType)
|
XCTAssertTrue(nativeType == .integer64AttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -159,23 +160,23 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Count("attribute", As: "alias")
|
let term = SelectTerm<NSManagedObject>.count("attribute", as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.Count("attribute", As: "alias"))
|
XCTAssertEqual(term, SelectTerm.count("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute", As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "count:")
|
XCTAssertEqual(function, "count:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .Integer64AttributeType)
|
XCTAssertTrue(nativeType == .integer64AttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -188,23 +189,23 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Maximum("attribute")
|
let term = SelectTerm<NSManagedObject>.maximum("attribute")
|
||||||
XCTAssertEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute", As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "max:")
|
XCTAssertEqual(function, "max:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "max(attribute)")
|
XCTAssertEqual(alias, "max(attribute)")
|
||||||
XCTAssertTrue(nativeType == .UndefinedAttributeType)
|
XCTAssertTrue(nativeType == .undefinedAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -212,23 +213,23 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Maximum("attribute", As: "alias")
|
let term = SelectTerm<NSManagedObject>.maximum("attribute", as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.Maximum("attribute", As: "alias"))
|
XCTAssertEqual(term, SelectTerm.maximum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute", As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "max:")
|
XCTAssertEqual(function, "max:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .UndefinedAttributeType)
|
XCTAssertTrue(nativeType == .undefinedAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -241,23 +242,23 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Minimum("attribute")
|
let term = SelectTerm<NSManagedObject>.minimum("attribute")
|
||||||
XCTAssertEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute", As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "min:")
|
XCTAssertEqual(function, "min:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "min(attribute)")
|
XCTAssertEqual(alias, "min(attribute)")
|
||||||
XCTAssertTrue(nativeType == .UndefinedAttributeType)
|
XCTAssertTrue(nativeType == .undefinedAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -265,23 +266,23 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Minimum("attribute", As: "alias")
|
let term = SelectTerm<NSManagedObject>.minimum("attribute", as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.Minimum("attribute", As: "alias"))
|
XCTAssertEqual(term, SelectTerm.minimum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute", As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "min:")
|
XCTAssertEqual(function, "min:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .UndefinedAttributeType)
|
XCTAssertTrue(nativeType == .undefinedAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -294,23 +295,23 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Sum("attribute")
|
let term = SelectTerm<NSManagedObject>.sum("attribute")
|
||||||
XCTAssertEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertEqual(term, SelectTerm.sum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute", As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "sum:")
|
XCTAssertEqual(function, "sum:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "sum(attribute)")
|
XCTAssertEqual(alias, "sum(attribute)")
|
||||||
XCTAssertTrue(nativeType == .DecimalAttributeType)
|
XCTAssertTrue(nativeType == .decimalAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -318,23 +319,23 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.Sum("attribute", As: "alias")
|
let term = SelectTerm<NSManagedObject>.sum("attribute", as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.Sum("attribute", As: "alias"))
|
XCTAssertEqual(term, SelectTerm.sum("attribute", as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute", As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute2"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
|
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||||
XCTAssertEqual(function, "sum:")
|
XCTAssertEqual(function, "sum:")
|
||||||
XCTAssertEqual(keyPath, "attribute")
|
XCTAssertEqual(keyPath, "attribute")
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .DecimalAttributeType)
|
XCTAssertTrue(nativeType == .decimalAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -347,20 +348,20 @@ final class SelectTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.ObjectID()
|
let term = SelectTerm<NSManagedObject>.objectID()
|
||||||
XCTAssertEqual(term, SelectTerm.ObjectID())
|
XCTAssertEqual(term, SelectTerm.objectID())
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID(As: "alias"))
|
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Identity(let alias, let nativeType):
|
case ._identity(let alias, let nativeType):
|
||||||
XCTAssertEqual(alias, "objectID")
|
XCTAssertEqual(alias, "objectID")
|
||||||
XCTAssertTrue(nativeType == .ObjectIDAttributeType)
|
XCTAssertTrue(nativeType == .objectIDAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -368,21 +369,21 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let term = SelectTerm.ObjectID(As: "alias")
|
let term = SelectTerm<NSManagedObject>.objectID(as: "alias")
|
||||||
XCTAssertEqual(term, SelectTerm.ObjectID(As: "alias"))
|
XCTAssertEqual(term, SelectTerm.objectID(as: "alias"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID(As: "alias2"))
|
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias2"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.ObjectID())
|
XCTAssertNotEqual(term, SelectTerm.objectID())
|
||||||
XCTAssertNotEqual(term, SelectTerm.Attribute("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Average("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.average("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Count("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.count("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Maximum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.maximum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Minimum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.minimum("attribute"))
|
||||||
XCTAssertNotEqual(term, SelectTerm.Sum("attribute"))
|
XCTAssertNotEqual(term, SelectTerm.sum("attribute"))
|
||||||
switch term {
|
switch term {
|
||||||
|
|
||||||
case ._Identity(let alias, let nativeType):
|
case ._identity(let alias, let nativeType):
|
||||||
XCTAssertEqual(alias, "alias")
|
XCTAssertEqual(alias, "alias")
|
||||||
XCTAssertTrue(nativeType == .ObjectIDAttributeType)
|
XCTAssertTrue(nativeType == .objectIDAttributeType)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
XCTFail()
|
XCTFail()
|
||||||
@@ -393,12 +394,12 @@ final class SelectTests: XCTestCase {
|
|||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatSelectClauses_ConfigureCorrectly() {
|
dynamic func test_ThatSelectClauses_ConfigureCorrectly() {
|
||||||
|
|
||||||
let term1 = SelectTerm.Attribute("attribute1")
|
let term1 = SelectTerm<NSManagedObject>.attribute("attribute1")
|
||||||
let term2 = SelectTerm.Attribute("attribute2")
|
let term2 = SelectTerm<NSManagedObject>.attribute("attribute2")
|
||||||
let term3 = SelectTerm.Attribute("attribute3")
|
let term3 = SelectTerm<NSManagedObject>.attribute("attribute3")
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let select = Select<Int>(term1, term2, term3)
|
let select = Select<NSManagedObject, Int>(term1, term2, term3)
|
||||||
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
||||||
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
||||||
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
||||||
@@ -408,7 +409,7 @@ final class SelectTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let select = Select<Int>([term1, term2, term3])
|
let select = Select<NSManagedObject, Int>([term1, term2, term3])
|
||||||
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
XCTAssertEqual(select.selectTerms, [term1, term2, term3])
|
||||||
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
|
||||||
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SetupTests.swift
|
// SetupTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,52 +23,57 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
// MARK: - SetupTests
|
// MARK: - SetupTests
|
||||||
|
|
||||||
class SetupTests: BaseTestCase {
|
class SetupTests: BaseTestDataTestCase {
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatDataStacks_ConfigureCorrectly() {
|
dynamic func test_ThatDataStacks_ConfigureCorrectly() {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let model = NSManagedObjectModel.mergedModelFromBundles([NSBundle(forClass: self.dynamicType)])!
|
let schemaHistory = SchemaHistory(
|
||||||
|
XcodeDataModelSchema.from(
|
||||||
let stack = DataStack(model: model, migrationChain: nil)
|
modelName: "Model",
|
||||||
XCTAssertEqual(stack.coordinator.managedObjectModel, model)
|
bundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
let stack = DataStack(schemaHistory: schemaHistory)
|
||||||
|
XCTAssertEqual(stack.coordinator.managedObjectModel, schemaHistory.rawModel)
|
||||||
XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator)
|
XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator)
|
||||||
XCTAssertNil(stack.rootSavingContext.parentContext)
|
XCTAssertNil(stack.rootSavingContext.parent)
|
||||||
XCTAssertEqual(stack.mainContext.parentContext, stack.rootSavingContext)
|
XCTAssertFalse(stack.rootSavingContext.isDataStackContext)
|
||||||
XCTAssertEqual(stack.model, model)
|
XCTAssertFalse(stack.rootSavingContext.isTransactionContext)
|
||||||
XCTAssertTrue(stack.migrationChain.valid)
|
XCTAssertEqual(stack.mainContext.parent, stack.rootSavingContext)
|
||||||
XCTAssertTrue(stack.migrationChain.empty)
|
XCTAssertTrue(stack.mainContext.isDataStackContext)
|
||||||
XCTAssertTrue(stack.migrationChain.rootVersions.isEmpty)
|
XCTAssertFalse(stack.mainContext.isTransactionContext)
|
||||||
XCTAssertTrue(stack.migrationChain.leafVersions.isEmpty)
|
XCTAssertEqual(stack.schemaHistory.rawModel, schemaHistory.rawModel)
|
||||||
|
XCTAssertTrue(stack.schemaHistory.migrationChain.isValid)
|
||||||
CoreStore.defaultStack = stack
|
XCTAssertTrue(stack.schemaHistory.migrationChain.isEmpty)
|
||||||
XCTAssertEqual(CoreStore.defaultStack, stack)
|
XCTAssertTrue(stack.schemaHistory.migrationChain.rootVersions.isEmpty)
|
||||||
|
XCTAssertTrue(stack.schemaHistory.migrationChain.leafVersions.isEmpty)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let migrationChain: MigrationChain = ["version1", "version2", "version3"]
|
let migrationChain: MigrationChain = ["version1", "version2", "version3", "Model"]
|
||||||
|
|
||||||
let stack = self.expectLogger([.LogWarning]) {
|
let stack = self.expectLogger([.logWarning]) {
|
||||||
|
|
||||||
DataStack(
|
DataStack(
|
||||||
modelName: "Model",
|
xcodeModelName: "Model",
|
||||||
bundle: NSBundle(forClass: self.dynamicType),
|
bundle: Bundle(for: Self.self),
|
||||||
migrationChain: migrationChain
|
migrationChain: migrationChain
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
XCTAssertEqual(stack.modelVersion, "Model")
|
XCTAssertEqual(stack.modelVersion, "Model")
|
||||||
XCTAssertEqual(stack.migrationChain, migrationChain)
|
XCTAssertEqual(stack.schemaHistory.migrationChain, migrationChain)
|
||||||
|
|
||||||
CoreStore.defaultStack = stack
|
|
||||||
XCTAssertEqual(CoreStore.defaultStack, stack)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,8 +81,8 @@ class SetupTests: BaseTestCase {
|
|||||||
dynamic func test_ThatInMemoryStores_SetupCorrectly() {
|
dynamic func test_ThatInMemoryStores_SetupCorrectly() {
|
||||||
|
|
||||||
let stack = DataStack(
|
let stack = DataStack(
|
||||||
modelName: "Model",
|
xcodeModelName: "Model",
|
||||||
bundle: NSBundle(forClass: self.dynamicType)
|
bundle: Bundle(for: Self.self)
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -131,8 +136,8 @@ class SetupTests: BaseTestCase {
|
|||||||
dynamic func test_ThatSQLiteStores_SetupCorrectly() {
|
dynamic func test_ThatSQLiteStores_SetupCorrectly() {
|
||||||
|
|
||||||
let stack = DataStack(
|
let stack = DataStack(
|
||||||
modelName: "Model",
|
xcodeModelName: "Model",
|
||||||
bundle: NSBundle(forClass: self.dynamicType)
|
bundle: Bundle(for: Self.self)
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -154,7 +159,7 @@ class SetupTests: BaseTestCase {
|
|||||||
let sqliteStore = SQLiteStore(
|
let sqliteStore = SQLiteStore(
|
||||||
fileName: "ConfigStore1.sqlite",
|
fileName: "ConfigStore1.sqlite",
|
||||||
configuration: "Config1",
|
configuration: "Config1",
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -173,7 +178,7 @@ class SetupTests: BaseTestCase {
|
|||||||
let sqliteStore = SQLiteStore(
|
let sqliteStore = SQLiteStore(
|
||||||
fileName: "ConfigStore2.sqlite",
|
fileName: "ConfigStore2.sqlite",
|
||||||
configuration: "Config2",
|
configuration: "Config2",
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -189,16 +194,74 @@ class SetupTests: BaseTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func test_ThatSQLiteStores_DeleteFilesCorrectly() {
|
||||||
|
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let sqliteStore = SQLiteStore()
|
||||||
|
func createStore() throws -> [String: Any] {
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
let stack = DataStack(
|
||||||
|
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("-shm")))
|
||||||
|
|
||||||
|
return try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||||
|
ofType: type(of: sqliteStore).storeType,
|
||||||
|
at: sqliteStore.fileURL,
|
||||||
|
options: sqliteStore.storeOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
|
||||||
|
let metadata = try createStore()
|
||||||
|
let stack = DataStack(
|
||||||
|
xcodeModelName: "Model",
|
||||||
|
bundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
try sqliteStore.cs_eraseStorageAndWait(
|
||||||
|
metadata: metadata,
|
||||||
|
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
|
||||||
|
)
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
|
||||||
|
let metadata = try createStore()
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatLegacySQLiteStores_SetupCorrectly() {
|
dynamic func test_ThatLegacySQLiteStores_SetupCorrectly() {
|
||||||
|
|
||||||
let stack = DataStack(
|
let stack = DataStack(
|
||||||
modelName: "Model",
|
xcodeModelName: "Model",
|
||||||
bundle: NSBundle(forClass: self.dynamicType)
|
bundle: Bundle(for: Self.self)
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sqliteStore = SQLiteStore()
|
let sqliteStore = SQLiteStore.legacy()
|
||||||
do {
|
do {
|
||||||
|
|
||||||
try stack.addStorageAndWait(sqliteStore)
|
try stack.addStorageAndWait(sqliteStore)
|
||||||
@@ -213,10 +276,10 @@ class SetupTests: BaseTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sqliteStore = SQLiteStore(
|
let sqliteStore = SQLiteStore.legacy(
|
||||||
fileName: "ConfigStore1.sqlite",
|
fileName: "ConfigStore1.sqlite",
|
||||||
configuration: "Config1",
|
configuration: "Config1",
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -232,10 +295,10 @@ class SetupTests: BaseTestCase {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let sqliteStore = SQLiteStore(
|
let sqliteStore = SQLiteStore.legacy(
|
||||||
fileName: "ConfigStore2.sqlite",
|
fileName: "ConfigStore2.sqlite",
|
||||||
configuration: "Config2",
|
configuration: "Config2",
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
|
|
||||||
@@ -250,4 +313,69 @@ class SetupTests: BaseTestCase {
|
|||||||
XCTAssert(sqliteStore.matchesPersistentStore(persistentStore!))
|
XCTAssert(sqliteStore.matchesPersistentStore(persistentStore!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func test_ThatLegacySQLiteStores_DeleteFilesCorrectly() {
|
||||||
|
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let sqliteStore = SQLiteStore.legacy()
|
||||||
|
func createStore() throws -> [String: Any] {
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
let stack = DataStack(
|
||||||
|
xcodeModelName: "Model",
|
||||||
|
bundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
try! stack.addStorageAndWait(
|
||||||
|
SQLiteStore.legacy(
|
||||||
|
fileName: sqliteStore.fileURL.lastPathComponent,
|
||||||
|
configuration: sqliteStore.configuration,
|
||||||
|
migrationMappingProviders: sqliteStore.migrationMappingProviders,
|
||||||
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.prepareTestDataForStack(stack)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
|
||||||
|
|
||||||
|
return try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||||
|
ofType: type(of: sqliteStore).storeType,
|
||||||
|
at: sqliteStore.fileURL,
|
||||||
|
options: sqliteStore.storeOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
|
||||||
|
let metadata = try createStore()
|
||||||
|
let stack = DataStack(
|
||||||
|
xcodeModelName: "Model",
|
||||||
|
bundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
try sqliteStore.cs_eraseStorageAndWait(
|
||||||
|
metadata: metadata,
|
||||||
|
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
|
||||||
|
)
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
|
||||||
|
let metadata = try createStore()
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
XCTFail()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// StorageInterfaceTests.swift
|
// StorageInterfaceTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -37,7 +38,7 @@ final class StorageInterfaceTests: XCTestCase {
|
|||||||
dynamic func test_ThatDefaultInMemoryStores_ConfigureCorrectly() {
|
dynamic func test_ThatDefaultInMemoryStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let store = InMemoryStore()
|
let store = InMemoryStore()
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSInMemoryStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSInMemoryStoreType)
|
||||||
XCTAssertNil(store.configuration)
|
XCTAssertNil(store.configuration)
|
||||||
XCTAssertNil(store.storeOptions)
|
XCTAssertNil(store.storeOptions)
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ final class StorageInterfaceTests: XCTestCase {
|
|||||||
dynamic func test_ThatCustomInMemoryStores_ConfigureCorrectly() {
|
dynamic func test_ThatCustomInMemoryStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let store = InMemoryStore(configuration: "config1")
|
let store = InMemoryStore(configuration: "config1")
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSInMemoryStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSInMemoryStoreType)
|
||||||
XCTAssertEqual(store.configuration, "config1")
|
XCTAssertEqual(store.configuration, "config1")
|
||||||
XCTAssertNil(store.storeOptions)
|
XCTAssertNil(store.storeOptions)
|
||||||
}
|
}
|
||||||
@@ -55,24 +56,23 @@ final class StorageInterfaceTests: XCTestCase {
|
|||||||
dynamic func test_ThatSQLiteStoreDefaultDirectories_AreCorrect() {
|
dynamic func test_ThatSQLiteStoreDefaultDirectories_AreCorrect() {
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory
|
let systemDirectorySearchPath = FileManager.SearchPathDirectory.cachesDirectory
|
||||||
#else
|
#else
|
||||||
let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory
|
let systemDirectorySearchPath = FileManager.SearchPathDirectory.applicationSupportDirectory
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let defaultSystemDirectory = NSFileManager
|
let defaultSystemDirectory = FileManager.default
|
||||||
.defaultManager()
|
.urls(for: systemDirectorySearchPath, in: .userDomainMask).first!
|
||||||
.URLsForDirectory(systemDirectorySearchPath, inDomains: .UserDomainMask).first!
|
|
||||||
|
|
||||||
let defaultRootDirectory = defaultSystemDirectory.URLByAppendingPathComponent(
|
let defaultRootDirectory = defaultSystemDirectory.appendingPathComponent(
|
||||||
NSBundle.mainBundle().bundleIdentifier ?? "com.CoreStore.DataStack",
|
Bundle.main.bundleIdentifier ?? "com.CoreStore.DataStack",
|
||||||
isDirectory: true
|
isDirectory: true
|
||||||
)
|
)
|
||||||
let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData"
|
let applicationName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String) ?? "CoreData"
|
||||||
|
|
||||||
let defaultFileURL = defaultRootDirectory
|
let defaultFileURL = defaultRootDirectory
|
||||||
.URLByAppendingPathComponent(applicationName, isDirectory: false)
|
.appendingPathComponent(applicationName, isDirectory: false)
|
||||||
.URLByAppendingPathExtension("sqlite")
|
.appendingPathExtension("sqlite")
|
||||||
|
|
||||||
XCTAssertEqual(SQLiteStore.defaultRootDirectory, defaultRootDirectory)
|
XCTAssertEqual(SQLiteStore.defaultRootDirectory, defaultRootDirectory)
|
||||||
XCTAssertEqual(SQLiteStore.defaultFileURL, defaultFileURL)
|
XCTAssertEqual(SQLiteStore.defaultFileURL, defaultFileURL)
|
||||||
@@ -82,137 +82,140 @@ final class StorageInterfaceTests: XCTestCase {
|
|||||||
dynamic func test_ThatDefaultSQLiteStores_ConfigureCorrectly() {
|
dynamic func test_ThatDefaultSQLiteStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let store = SQLiteStore()
|
let store = SQLiteStore()
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||||
XCTAssertNil(store.configuration)
|
XCTAssertNil(store.configuration)
|
||||||
XCTAssertEqual(store.storeOptions, [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.fileURL, SQLiteStore.defaultFileURL)
|
||||||
XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles())
|
XCTAssertTrue(store.migrationMappingProviders.isEmpty)
|
||||||
XCTAssertEqual(store.localStorageOptions, [.None])
|
XCTAssertEqual(store.localStorageOptions, .none)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatFileURLSQLiteStores_ConfigureCorrectly() {
|
dynamic func test_ThatFileURLSQLiteStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
|
let fileURL = FileManager.default.temporaryDirectory
|
||||||
.URLByAppendingPathComponent(NSUUID().UUIDString, isDirectory: false)
|
.appendingPathComponent(UUID().uuidString, isDirectory: false)
|
||||||
.URLByAppendingPathExtension("db")
|
.appendingPathExtension("db")
|
||||||
let bundles = [NSBundle(forClass: self.dynamicType)]
|
let mappingProvider = XcodeSchemaMappingProvider(
|
||||||
|
from: "V1", to: "V2",
|
||||||
|
mappingModelBundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
|
||||||
let store = SQLiteStore(
|
let store = SQLiteStore(
|
||||||
fileURL: fileURL,
|
fileURL: fileURL,
|
||||||
configuration: "config1",
|
configuration: "config1",
|
||||||
mappingModelBundles: bundles,
|
migrationMappingProviders: [mappingProvider],
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||||
XCTAssertEqual(store.configuration, "config1")
|
XCTAssertEqual(store.configuration, "config1")
|
||||||
XCTAssertEqual(store.storeOptions, [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.fileURL, fileURL)
|
||||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||||
XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
|
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() {
|
dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let fileName = NSUUID().UUIDString + ".db"
|
let fileName = UUID().uuidString + ".db"
|
||||||
let bundles = [NSBundle(forClass: self.dynamicType)]
|
let mappingProvider = XcodeSchemaMappingProvider(
|
||||||
|
from: "V1", to: "V2",
|
||||||
|
mappingModelBundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
let store = SQLiteStore(
|
let store = SQLiteStore(
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
configuration: "config1",
|
configuration: "config1",
|
||||||
mappingModelBundles: bundles,
|
migrationMappingProviders: [mappingProvider],
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||||
XCTAssertEqual(store.configuration, "config1")
|
XCTAssertEqual(store.configuration, "config1")
|
||||||
XCTAssertEqual(store.storeOptions, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
XCTAssertEqual(
|
||||||
|
store.storeOptions as NSDictionary?,
|
||||||
|
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||||
|
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||||
|
)
|
||||||
|
|
||||||
XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, SQLiteStore.defaultRootDirectory)
|
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.defaultRootDirectory)
|
||||||
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
||||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||||
XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
|
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatLegacySQLiteStoreDefaultDirectories_AreCorrect() {
|
dynamic func test_ThatLegacySQLiteStoreDefaultDirectories_AreCorrect() {
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory
|
let systemDirectorySearchPath = FileManager.SearchPathDirectory.cachesDirectory
|
||||||
#else
|
#else
|
||||||
let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory
|
let systemDirectorySearchPath = FileManager.SearchPathDirectory.applicationSupportDirectory
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let legacyDefaultRootDirectory = NSFileManager.defaultManager().URLsForDirectory(
|
let legacyDefaultRootDirectory = FileManager.default.urls(
|
||||||
systemDirectorySearchPath,
|
for: systemDirectorySearchPath,
|
||||||
inDomains: .UserDomainMask
|
in: .userDomainMask).first!
|
||||||
).first!
|
|
||||||
|
|
||||||
let legacyDefaultFileURL = legacyDefaultRootDirectory
|
let legacyDefaultFileURL = legacyDefaultRootDirectory
|
||||||
.URLByAppendingPathComponent(DataStack.applicationName, isDirectory: false)
|
.appendingPathComponent(DataStack.applicationName, isDirectory: false)
|
||||||
.URLByAppendingPathExtension("sqlite")
|
.appendingPathExtension("sqlite")
|
||||||
|
|
||||||
XCTAssertEqual(LegacySQLiteStore.defaultRootDirectory, legacyDefaultRootDirectory)
|
XCTAssertEqual(SQLiteStore.legacyDefaultRootDirectory, legacyDefaultRootDirectory)
|
||||||
XCTAssertEqual(LegacySQLiteStore.defaultFileURL, legacyDefaultFileURL)
|
XCTAssertEqual(SQLiteStore.legacyDefaultFileURL, legacyDefaultFileURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() {
|
dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let store = LegacySQLiteStore()
|
let store = SQLiteStore.legacy()
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||||
XCTAssertNil(store.configuration)
|
XCTAssertNil(store.configuration)
|
||||||
XCTAssertEqual(store.storeOptions, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
XCTAssertEqual(
|
||||||
|
store.storeOptions as NSDictionary?,
|
||||||
XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL)
|
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||||
XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles())
|
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||||
XCTAssertEqual(store.localStorageOptions, [.None])
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
|
||||||
dynamic func test_ThatFileURLLegacySQLiteStores_ConfigureCorrectly() {
|
|
||||||
|
|
||||||
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
|
|
||||||
.URLByAppendingPathComponent(NSUUID().UUIDString, isDirectory: false)
|
|
||||||
.URLByAppendingPathExtension("db")
|
|
||||||
let bundles = [NSBundle(forClass: self.dynamicType)]
|
|
||||||
|
|
||||||
let store = LegacySQLiteStore(
|
|
||||||
fileURL: fileURL,
|
|
||||||
configuration: "config1",
|
|
||||||
mappingModelBundles: bundles,
|
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
|
||||||
)
|
)
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
|
||||||
XCTAssertEqual(store.configuration, "config1")
|
|
||||||
XCTAssertEqual(store.storeOptions, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
|
||||||
|
|
||||||
XCTAssertEqual(store.fileURL, fileURL)
|
XCTAssertEqual(store.fileURL, SQLiteStore.legacyDefaultFileURL)
|
||||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
XCTAssertTrue(store.migrationMappingProviders.isEmpty)
|
||||||
XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
|
XCTAssertEqual(store.localStorageOptions, .none)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() {
|
dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() {
|
||||||
|
|
||||||
let fileName = NSUUID().UUIDString + ".db"
|
let fileName = UUID().uuidString + ".db"
|
||||||
let bundles = [NSBundle(forClass: self.dynamicType)]
|
let mappingProvider = XcodeSchemaMappingProvider(
|
||||||
|
from: "V1", to: "V2",
|
||||||
let store = LegacySQLiteStore(
|
mappingModelBundle: Bundle(for: Self.self)
|
||||||
|
)
|
||||||
|
let store = SQLiteStore.legacy(
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
configuration: "config1",
|
configuration: "config1",
|
||||||
mappingModelBundles: bundles,
|
migrationMappingProviders: [mappingProvider],
|
||||||
localStorageOptions: .RecreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
|
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
|
||||||
XCTAssertEqual(store.configuration, "config1")
|
XCTAssertEqual(store.configuration, "config1")
|
||||||
XCTAssertEqual(store.storeOptions, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
|
XCTAssertEqual(
|
||||||
|
store.storeOptions as NSDictionary?,
|
||||||
|
[NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||||
|
NSBinaryStoreInsecureDecodingCompatibilityOption: true] as NSDictionary
|
||||||
|
)
|
||||||
|
|
||||||
XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, LegacySQLiteStore.defaultRootDirectory)
|
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.legacyDefaultRootDirectory)
|
||||||
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
|
||||||
XCTAssertEqual(store.mappingModelBundles, bundles)
|
XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
|
||||||
XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
|
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// TestEntity1.swift
|
// TestEntity1.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2014 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,9 +31,11 @@ class TestEntity1: NSManagedObject {
|
|||||||
@NSManaged var testEntityID: NSNumber?
|
@NSManaged var testEntityID: NSNumber?
|
||||||
@NSManaged var testString: String?
|
@NSManaged var testString: String?
|
||||||
@NSManaged var testNumber: NSNumber?
|
@NSManaged var testNumber: NSNumber?
|
||||||
@NSManaged var testDate: NSDate?
|
@NSManaged var testDate: Date?
|
||||||
@NSManaged var testBoolean: NSNumber?
|
@NSManaged var testBoolean: NSNumber?
|
||||||
@NSManaged var testDecimal: NSDecimalNumber?
|
@NSManaged var testDecimal: NSDecimalNumber?
|
||||||
@NSManaged var testData: NSData?
|
@NSManaged var testData: Data?
|
||||||
@NSManaged var testNil: String?
|
@NSManaged var testNil: String?
|
||||||
|
@NSManaged var testToOne: TestEntity1?
|
||||||
|
@NSManaged var testToManyUnordered: NSSet?
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// TestEntity1.swift
|
// TestEntity1.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2014 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,9 +31,11 @@ class TestEntity2: NSManagedObject {
|
|||||||
@NSManaged var testEntityID: NSNumber?
|
@NSManaged var testEntityID: NSNumber?
|
||||||
@NSManaged var testString: String?
|
@NSManaged var testString: String?
|
||||||
@NSManaged var testNumber: NSNumber?
|
@NSManaged var testNumber: NSNumber?
|
||||||
@NSManaged var testDate: NSDate?
|
@NSManaged var testDate: Date?
|
||||||
@NSManaged var testBoolean: NSNumber?
|
@NSManaged var testBoolean: NSNumber?
|
||||||
@NSManaged var testDecimal: NSDecimalNumber?
|
@NSManaged var testDecimal: NSDecimalNumber?
|
||||||
@NSManaged var testData: NSData?
|
@NSManaged var testData: Data?
|
||||||
@NSManaged var testNil: String?
|
@NSManaged var testNil: String?
|
||||||
|
@NSManaged var testToOne: TestEntity2?
|
||||||
|
@NSManaged var testToManyOrdered: NSOrderedSet?
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// TweakTests.swift
|
// TweakTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
@@ -43,7 +44,7 @@ final class TweakTests: XCTestCase {
|
|||||||
$0.fetchLimit = 200
|
$0.fetchLimit = 200
|
||||||
$0.predicate = predicate
|
$0.predicate = predicate
|
||||||
}
|
}
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
tweak.applyToFetchRequest(request)
|
tweak.applyToFetchRequest(request)
|
||||||
XCTAssertEqual(request.fetchOffset, 100)
|
XCTAssertEqual(request.fetchOffset, 100)
|
||||||
XCTAssertEqual(request.fetchLimit, 200)
|
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
|
// WhereTests.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Copyright © 2016 John Rommel Estropia
|
// Copyright © 2018 John Rommel Estropia
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,128 +23,581 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
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
|
//MARK: - WhereTests
|
||||||
|
|
||||||
final class WhereTests: XCTestCase {
|
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
|
@objc
|
||||||
dynamic func test_ThatWhereClauses_ConfigureCorrectly() {
|
dynamic func test_ThatWhereClauses_ConfigureCorrectly() {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let whereClause = Where()
|
let whereClause = Where<NSManagedObject>()
|
||||||
XCTAssertEqual(whereClause, Where(true))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(true))
|
||||||
XCTAssertNotEqual(whereClause, Where(false))
|
XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
|
||||||
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
|
XCTAssertAllEqual(whereClause.predicate, NSPredicate(value: true))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let whereClause = Where(true)
|
let whereClause = Where<NSManagedObject>(true)
|
||||||
XCTAssertEqual(whereClause, Where())
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>())
|
||||||
XCTAssertNotEqual(whereClause, Where(false))
|
XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
|
||||||
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
|
XCTAssertAllEqual(whereClause.predicate, NSPredicate(value: true))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||||
let whereClause = Where(predicate)
|
let whereClause = Where<NSManagedObject>(predicate)
|
||||||
XCTAssertEqual(whereClause, Where(predicate))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||||
XCTAssertEqual(whereClause.predicate, predicate)
|
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let whereClause = Where("%K == %@", "key", "value")
|
let whereClause = Where<NSManagedObject>("%K == %@", "key", "value")
|
||||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||||
XCTAssertEqual(whereClause, Where(predicate))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||||
XCTAssertEqual(whereClause.predicate, predicate)
|
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let whereClause = Where("%K == %@", argumentArray: ["key", "value"])
|
let whereClause = Where<NSManagedObject>("%K == %@", argumentArray: ["key", "value"])
|
||||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||||
XCTAssertEqual(whereClause, Where(predicate))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||||
XCTAssertEqual(whereClause.predicate, predicate)
|
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let whereClause = Where("key", isEqualTo: "value")
|
let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
|
||||||
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
let predicate = NSPredicate(format: "%K == %@", "key", "value")
|
||||||
XCTAssertEqual(whereClause, Where(predicate))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||||
XCTAssertEqual(whereClause.predicate, predicate)
|
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||||
}
|
}
|
||||||
do {
|
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"])
|
let predicate = NSPredicate(format: "%K IN %@", "key", ["value1", "value2", "value3"])
|
||||||
XCTAssertEqual(whereClause, Where(predicate))
|
XCTAssertAllEqual(whereClause, Where<NSManagedObject>(predicate))
|
||||||
XCTAssertEqual(whereClause.predicate, predicate)
|
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func test_ThatWhereClauses_BridgeArgumentsCorrectly() {
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
let value: Int = 100
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
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"
|
||||||
|
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")
|
||||||
|
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]
|
||||||
|
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]
|
||||||
|
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
|
@objc
|
||||||
dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() {
|
dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() {
|
||||||
|
|
||||||
let whereClause1 = Where("key1", isEqualTo: "value1")
|
let whereClause1 = Where<NSManagedObject>("key1", isEqualTo: "value1")
|
||||||
let whereClause2 = Where("key2", isEqualTo: "value2")
|
let whereClause2 = Where<NSManagedObject>("key2", isEqualTo: "value2")
|
||||||
let whereClause3 = Where("key3", isEqualTo: "value3")
|
let whereClause3 = Where<NSManagedObject>("key3", isEqualTo: "value3")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let notWhere = !whereClause1
|
let notWhere = !whereClause1
|
||||||
let notPredicate = NSCompoundPredicate(
|
let notPredicate = NSCompoundPredicate(
|
||||||
type: .NotPredicateType,
|
type: .not,
|
||||||
subpredicates: [whereClause1.predicate]
|
subpredicates: [whereClause1.predicate]
|
||||||
)
|
)
|
||||||
XCTAssertEqual(notWhere.predicate, notPredicate)
|
XCTAssertAllEqual(notWhere.predicate, notPredicate)
|
||||||
XCTAssertEqual(notWhere, !whereClause1)
|
XCTAssertAllEqual(notWhere, !whereClause1)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
let andWhere = whereClause1 && whereClause2 && whereClause3
|
let andWhere = whereClause1 && whereClause2 && whereClause3
|
||||||
let andPredicate = NSCompoundPredicate(
|
let andPredicate = NSCompoundPredicate(
|
||||||
type: .AndPredicateType,
|
type: .and,
|
||||||
subpredicates: [
|
subpredicates: [
|
||||||
NSCompoundPredicate(
|
NSCompoundPredicate(
|
||||||
type: .AndPredicateType,
|
type: .and,
|
||||||
subpredicates: [whereClause1.predicate, whereClause2.predicate]
|
subpredicates: [whereClause1.predicate, whereClause2.predicate]
|
||||||
),
|
),
|
||||||
whereClause3.predicate
|
whereClause3.predicate
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
XCTAssertEqual(andWhere.predicate, andPredicate)
|
XCTAssertAllEqual(andWhere.predicate, andPredicate)
|
||||||
XCTAssertEqual(andWhere, whereClause1 && whereClause2 && whereClause3)
|
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 {
|
do {
|
||||||
|
|
||||||
let orWhere = whereClause1 || whereClause2 || whereClause3
|
let orWhere = whereClause1 || whereClause2 || whereClause3
|
||||||
let orPredicate = NSCompoundPredicate(
|
let orPredicate = NSCompoundPredicate(
|
||||||
type: .OrPredicateType,
|
type: .or,
|
||||||
subpredicates: [
|
subpredicates: [
|
||||||
NSCompoundPredicate(
|
NSCompoundPredicate(
|
||||||
type: .OrPredicateType,
|
type: .or,
|
||||||
subpredicates: [whereClause1.predicate, whereClause2.predicate]
|
subpredicates: [whereClause1.predicate, whereClause2.predicate]
|
||||||
),
|
),
|
||||||
whereClause3.predicate
|
whereClause3.predicate
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
XCTAssertEqual(orWhere.predicate, orPredicate)
|
XCTAssertAllEqual(orWhere.predicate, orPredicate)
|
||||||
XCTAssertEqual(orWhere, whereClause1 || whereClause2 || whereClause3)
|
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
|
@objc
|
||||||
dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() {
|
dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() {
|
||||||
|
|
||||||
let whereClause = Where("key", isEqualTo: "value")
|
let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
|
||||||
let request = NSFetchRequest()
|
let request = Internals.CoreStoreFetchRequest<NSFetchRequestResult>()
|
||||||
whereClause.applyToFetchRequest(request)
|
whereClause.applyToFetchRequest(request)
|
||||||
XCTAssertNotNil(request.predicate)
|
XCTAssertNotNil(request.predicate)
|
||||||
XCTAssertEqual(request.predicate, whereClause.predicate)
|
XCTAssertAllEqual(request.predicate, whereClause.predicate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1038
Demo/Demo.xcodeproj/project.pbxproj
Normal file
78
Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1200"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||||
|
BuildableName = "Demo.app"
|
||||||
|
BlueprintName = "Demo"
|
||||||
|
ReferencedContainer = "container:Demo.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||||
|
BuildableName = "Demo.app"
|
||||||
|
BlueprintName = "Demo"
|
||||||
|
ReferencedContainer = "container:Demo.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
|
||||||
|
BuildableName = "Demo.app"
|
||||||
|
BlueprintName = "Demo"
|
||||||
|
ReferencedContainer = "container:Demo.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
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()
|
||||||
|
}
|
||||||
|
}
|
||||||