mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-21 17:09:42 +01:00
Update README.md
This commit is contained in:
155
README.md
155
README.md
@@ -1143,14 +1143,16 @@ Take special care when implementing `CoreStoreLogger`'s `assert(...)` and `abort
|
|||||||
- `abort(...)`: This method is *the* last-chance for your app to *synchronously* log a fatal error within CoreStore. The app will be terminated right after this function is called (CoreStore calls `fatalError()` internally)
|
- `abort(...)`: This method is *the* last-chance for your app to *synchronously* log a fatal error within CoreStore. The app will be terminated right after this function is called (CoreStore calls `fatalError()` internally)
|
||||||
|
|
||||||
Starting CoreStore 2.0, all CoreStore types now have very useful (and pretty formatted!) `print(...)` outputs.
|
Starting CoreStore 2.0, all CoreStore types now have very useful (and pretty formatted!) `print(...)` outputs.
|
||||||
A couples of examples; `ListMonitor`:
|
|
||||||
|
A couple of examples, `ListMonitor`:
|
||||||
|
|
||||||
<img width="369" alt="screen shot 2016-07-10 at 22 56 44" src="https://cloud.githubusercontent.com/assets/3029684/16713994/ae06e702-46f1-11e6-83a8-dee48b480bab.png" />
|
<img width="369" alt="screen shot 2016-07-10 at 22 56 44" src="https://cloud.githubusercontent.com/assets/3029684/16713994/ae06e702-46f1-11e6-83a8-dee48b480bab.png" />
|
||||||
|
|
||||||
`CoreStoreError.MappingModelNotFoundError`
|
`CoreStoreError.MappingModelNotFoundError`:
|
||||||
|
|
||||||
<img width="506" alt="MappingModelNotFoundError" src="https://cloud.githubusercontent.com/assets/3029684/16713962/e021f548-46f0-11e6-8100-f9b5ea6b4a08.png" />
|
<img width="506" alt="MappingModelNotFoundError" src="https://cloud.githubusercontent.com/assets/3029684/16713962/e021f548-46f0-11e6-8100-f9b5ea6b4a08.png" />
|
||||||
|
|
||||||
|
These are all implemented with `CustomDebugStringConvertible.debugDescription`, so they work with lldb's `po` command as well.
|
||||||
|
|
||||||
## Observing changes and notifications (unavailable on macOS)
|
## Observing changes and notifications (unavailable on macOS)
|
||||||
CoreStore provides type-safe wrappers for observing managed objects:
|
CoreStore provides type-safe wrappers for observing managed objects:
|
||||||
@@ -1285,12 +1287,139 @@ let person2 = self.monitor[1, 2]
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Objective-C support
|
## Objective-C support
|
||||||
<WIP>
|
CoreStore 2.0 was a big move to address the large number of apps starting to convert from Objective-C to Swift. The basic problem is this: The cost of converting all code base to Swift is very big, so most apps are forced to do undergo a *transitional* ObjC-Swift hybrid phase. This used to mean that these apps could not use the Swifty-est libraries out there yet, or that they may have to write their own bridging methods just to make new code usable in their old Objective-C code.
|
||||||
|
|
||||||
|
With 2.0, all CoreStore types are still written in pure Swift, but they now have Objective-C "bridging classes" that are visible to Objective-C code. To show a couple of usage examples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th>Swift</th><th>Objective-C</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre lang=swift>
|
||||||
|
try CoreStore.addStorageAndWait(SQLiteStore)
|
||||||
|
</pre></td>
|
||||||
|
<td><pre lang=objc>
|
||||||
|
NSError *error
|
||||||
|
[CSCoreStore addSQLiteStorageAndWait:[CSSQLiteStore new] error:&error]
|
||||||
|
</pre></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre lang=swift>
|
||||||
|
CoreStore.beginAsynchronous { (transaction) in
|
||||||
|
// ...
|
||||||
|
transaction.commit { (result) in
|
||||||
|
switch result {
|
||||||
|
case .Success(let hasChanges): print(hasChanges)
|
||||||
|
case .Failure(let error): print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></td>
|
||||||
|
<td><pre lang=objc>
|
||||||
|
[CSCoreStore beginAsynchronous:^(CSAsynchronousDataTransaction *transaction) {
|
||||||
|
// ...
|
||||||
|
[transaction commitWithCompletion:^(CSSaveResult *result) {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
NSLog(@"hasChanges: %d", result.hasChanges);
|
||||||
|
}
|
||||||
|
else if (result.isFailure) {
|
||||||
|
NSLog(@"error: %@", result.error);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
</pre></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
All of these `CS`-prefixed bridging classes have very similar usage to the existing CoreStore APIs, and ironically *none of them are written in Objective-C*. The secret is all in *CoreStoreBridge.swift*, where we see the signature of these bridging classes, the `CoreStoreObjectiveCType` protocol:
|
||||||
|
```swift
|
||||||
|
public protocol CoreStoreObjectiveCType: class, AnyObject {
|
||||||
|
associatedtype SwiftType
|
||||||
|
var bridgeToSwift: SwiftType { get }
|
||||||
|
init(_ swiftValue: SwiftType)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Notice that these bridging classes all hold a reference to their corresponding `SwiftType`.
|
||||||
|
|
||||||
|
Conversely, CoreStore original types implement the `CoreStoreSwiftType` protocol:
|
||||||
|
```swift
|
||||||
|
public protocol CoreStoreSwiftType {
|
||||||
|
associatedtype ObjectiveCType
|
||||||
|
var bridgeToObjectiveC: ObjectiveCType { get }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
These two protocols let CoreStore types free to bridge instances between Objective-C and Swift.
|
||||||
|
|
||||||
|
This is very different to the common approach where apps and libraries write Objective-C APIs just to support both Objective-C and Swift. The advantage with CoreStore's approach is that your Swift codebase can already use the purely-Swift API without further changes in the future, but your "hybrid" codebase can still bridge instances back and forth from Objective-C to Swift.
|
||||||
|
|
||||||
|
For example, you may have a new, modern Swift class that holds a `ListMonitor`:
|
||||||
|
```swift
|
||||||
|
class MyViewController: UIViewController {
|
||||||
|
let monitor = CoreStore.monitorList(From(MyEntity), ...)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Now let's say you have a legacy Objective-C class that previously uses `NSFetchedResultsController`. It's easy to switch from `NSFetchedResultsController` to `CSListMonitor`, but converting the rest of this huge class is impractical. You end up with
|
||||||
|
```objc
|
||||||
|
@interface MYOldViewController: UIViewController
|
||||||
|
@property (nonatomic, readonly, strong) CSListMonitor* monitor;
|
||||||
|
- (instancetype)initWithMonitor:(CSListMonitor *)monitor;
|
||||||
|
@end
|
||||||
|
```
|
||||||
|
When you need to instantiate this class from Swift, you just call `bridgeToObjectiveC`:
|
||||||
|
```swift
|
||||||
|
class MyViewController: UIViewController {
|
||||||
|
let monitor = CoreStore.monitorList(From(MyEntity), ...)
|
||||||
|
func showOldController() {
|
||||||
|
let controller = MYOldViewController(monitor: self.monitor.bridgeToObjectiveC)
|
||||||
|
self.presentViewController(controller, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note that the `CSListMonitor` holds the exact same `ListMonitor` instance, which means that no copies and no extra fetching occur.
|
||||||
|
|
||||||
|
### Objective-C syntax sugars
|
||||||
|
Objective-C tends to be verbose, so some method calls are long and unreadable. For example, fetching looks like this:
|
||||||
|
```objc
|
||||||
|
NSArray<MYPerson *> *objects =
|
||||||
|
[CSCoreStore
|
||||||
|
fetchAllFrom:[[CSFrom alloc] initWithEntityClass:[MYPerson class]]
|
||||||
|
fetchClauses:@[[[CSWhere alloc] initWithFormat:@"%K == %@", @"isHidden", @NO],
|
||||||
|
[[CSOrderBy alloc] initWithSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES],
|
||||||
|
[NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES]]]]];
|
||||||
|
```
|
||||||
|
Although it works, it looks terrible. For this, CoreStore provides *CoreStoreBridge.h* where these Objective-C calls are wrapped in readable, convenient macros and global functions. The call above becomes
|
||||||
|
```objc
|
||||||
|
NSArray<MYPerson *> *objects =
|
||||||
|
[CSCoreStore
|
||||||
|
fetchAllFrom:CSFromClass([MYPerson class])
|
||||||
|
fetchClauses:@[CSWhereFormat(@"%K == %@", @"isHidden", @NO),
|
||||||
|
CSOrderByKeys(CSSortAscending(@"lastName"),
|
||||||
|
CSSortAscending(@"firstName"), nil)]];
|
||||||
|
```
|
||||||
|
That's much shorter now. But we can still do better. Notice that we have strings being used as key paths. The `CSKeyPath(...)` macro gives us compile-time checking so keys that don't exist in a class will generate errors. Our key-safe code now looks like this:
|
||||||
|
```objc
|
||||||
|
NSArray<MYPerson *> *objects =
|
||||||
|
[CSCoreStore
|
||||||
|
fetchAllFrom:CSFromClass([MYPerson class])
|
||||||
|
fetchClauses:@[CSWhereFormat(@"%K == %@", CSKeyPath(MYPerson, isHidden), @NO),
|
||||||
|
CSOrderByKeys(CSSortAscending(CSKeyPath(MYPerson, lastName)),
|
||||||
|
CSSortAscending(CSKeyPath(MYPerson, firstName)), nil)]];
|
||||||
|
```
|
||||||
|
|
||||||
|
To use these syntax sugars, include *CoreStoreBridge.h* in your Objective-C source files. For projects that support iOS 7 (and thus cannot build CoreStore as a module), you will need to add
|
||||||
|
```
|
||||||
|
SWIFT_OBJC_INTERFACE_HEADER_NAME=$(SWIFT_OBJC_INTERFACE_HEADER_NAME)
|
||||||
|
```
|
||||||
|
to your target's `GCC_PREPROCESSOR_DEFINITIONS` build setting.
|
||||||
|
|
||||||
|
<img width="797" alt="GCC_PREPROCESSOR_DEFINITIONS" src="https://cloud.githubusercontent.com/assets/3029684/16714547/92497fc4-4701-11e6-81db-6b1a11743cc5.png" />
|
||||||
|
|
||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
- Support iCloud stores
|
- Built-in "singleton objects" support
|
||||||
- CoreSpotlight auto-indexing (experimental)
|
- Built-in "readonly" stores
|
||||||
|
- CoreSpotlight auto-indexing (experimenting, still some roadblocks ahead)
|
||||||
|
- Synching
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
@@ -1311,8 +1440,8 @@ This installs CoreStore as a framework. Declare `import CoreStore` in your swift
|
|||||||
### Install with Carthage
|
### Install with Carthage
|
||||||
In your `Cartfile`, add
|
In your `Cartfile`, add
|
||||||
```
|
```
|
||||||
github "JohnEstropia/CoreStore" >= 1.6.0
|
github "JohnEstropia/CoreStore" >= 2.0.0
|
||||||
github "JohnEstropia/GCDKit" >= 1.2.2
|
github "JohnEstropia/GCDKit" >= 1.2.5
|
||||||
```
|
```
|
||||||
and run
|
and run
|
||||||
```
|
```
|
||||||
@@ -1332,6 +1461,18 @@ Drag and drop **CoreStore.xcodeproj** to your project.
|
|||||||
Add all *.swift* files to your project.
|
Add all *.swift* files to your project.
|
||||||
|
|
||||||
|
|
||||||
|
### Objective-C support
|
||||||
|
|
||||||
|
To use the Objective-C syntax sugars, import *CoreStoreBridge.h* in your *.m* source files.
|
||||||
|
|
||||||
|
For projects that support iOS 7 (and thus cannot build CoreStore as a module), you will need to add
|
||||||
|
```
|
||||||
|
SWIFT_OBJC_INTERFACE_HEADER_NAME=$(SWIFT_OBJC_INTERFACE_HEADER_NAME)
|
||||||
|
```
|
||||||
|
to your target's `GCC_PREPROCESSOR_DEFINITIONS` build setting:
|
||||||
|
|
||||||
|
<img width="797" alt="GCC_PREPROCESSOR_DEFINITIONS" src="https://cloud.githubusercontent.com/assets/3029684/16714547/92497fc4-4701-11e6-81db-6b1a11743cc5.png" />
|
||||||
|
|
||||||
|
|
||||||
# Changesets
|
# Changesets
|
||||||
### Upgrading from 1.x.x to 2.x.x
|
### Upgrading from 1.x.x to 2.x.x
|
||||||
|
|||||||
Reference in New Issue
Block a user