Skip to content
This repository was archived by the owner on Dec 17, 2018. It is now read-only.

Commit b3ebeb2

Browse files
committed
Documentation migration (#1)
1 parent bc0d518 commit b3ebeb2

17 files changed

Lines changed: 243 additions & 60 deletions

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
osx_image: xcode8.3
22
language: objective-c
33

4+
branches:
5+
only:
6+
- master
7+
48
env:
59
global:
610
- PROJECT="CloudCore.xcodeproj"

CloudCore.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "CloudCore"
33
s.summary = "Framework that enables syncing between iCloud (CloudKit) and Core Data"
4-
s.version = "0.1"
4+
s.version = "0.1.1"
55
s.homepage = "https://github.com/sorix/CloudCore"
66
s.license = 'MIT'
77
s.author = { "Vasily Ulianov" => "vasily@me.com" }

README.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# CloudCore
22

33
[![Build Status](https://travis-ci.org/Sorix/CloudCore.svg?branch=master)](https://travis-ci.org/Sorix/CloudCore)
4-
[![Version](https://img.shields.io/cocoapods/v/CloudCore.svg?style=flat)](http://cocoadocs.org/docsets/CloudCore)
5-
[![Platform](https://img.shields.io/cocoapods/p/CloudCore.svg?style=flat)](http://cocoadocs.org/docsets/CloudCore)
4+
[![Documentation](https://img.shields.io/cocoapods/metrics/doc-percent/CloudCore.svg)](http://cocoadocs.org/docsets/CloudCore/)
5+
[![Version](https://img.shields.io/cocoapods/v/CloudCore.svg?style=flat)](https://cocoapods.org/pods/CloudCore)
6+
![Platform](https://img.shields.io/cocoapods/p/CloudCore.svg?style=flat)
67
![Status](https://img.shields.io/badge/status-alpha-red.svg)
78
![Swift](https://img.shields.io/badge/swift-3.0-orange.svg)
89

@@ -36,13 +37,17 @@ dependencies: [
3637
]
3738
```
3839

39-
## Quick start
40-
*Detailed documentation is available at [Wiki](https://github.com/Sorix/CloudCore/wiki).*
40+
## How to help?
41+
Current version of framework hasn't been deeply tested and may contain errors. If you can test framework, I will be very glad. If you found an error, please post [an issue](https://github.com/Sorix/CloudCore/issues).
42+
43+
## Documentation
44+
Detailed documentation is [available at CocoaDocs](http://cocoadocs.org/docsets/CloudCore/).
4145

46+
## Quick start
4247
1. Enable CloudKit capability for you application:
4348
![CloudKit capability](https://cloud.githubusercontent.com/assets/5610904/25092841/28305bc0-2398-11e7-9fbf-f94c619c264f.png)
4449

45-
2. Add 2 [service attributes](https://github.com/Sorix/CloudCore/wiki/Service-attributes) to each entity in CoreData model you want to sync:
50+
2. Add 2 service attributes to each entity in CoreData model you want to sync:
4651
* `recordData` attribute with `Binary` type
4752
* `recordID` attribute with `String` type
4853

@@ -77,6 +82,32 @@ func applicationDidEnterBackground(_ application: UIApplication) {
7782

7883
4. Make first run of your application in development environment, fill example data in Core Data and wait for syncing. CloudCore will create needed CloudKit schemes automatically.
7984

85+
## Service attributes
86+
CloudCore stores service CloudKit information in managed objects, you need to add that attributes to your Core Data model. If required attributes are not found in entity that entity won't be synced.
87+
88+
Required attributes for each synced entity:
89+
1. *Record Data* attribute with `Binary` type
90+
2. *Record ID* attribute with `String` type
91+
92+
You may specify attribute's names in 2 ways (you may combine that ways in different entities).
93+
94+
### User Info
95+
First off CloudCore try to search attributes by analyzing User Info at your model, you may specify attribute's key as `CloudCoreType` to mark that attribute as service one. Values are:
96+
* *Record Data* value is `recordData`.
97+
* *Record ID* value is `recordID`.
98+
99+
![Model editor User Info](https://cloud.githubusercontent.com/assets/5610904/24004400/52e0ff94-0a77-11e7-9dd9-e1e24a86add5.png)
100+
101+
### Default names
102+
The most simple way is to name attributes with default names because you don't need to specify User Info. Default names are configured at [[configuration struct|Configuration]], if you haven't changed them it will be `recordID` and `recordData`.
103+
104+
Remember that User Info always have a priority, so if User Info is founded for that attribute type it will be used instead of default naming.
105+
106+
### 💡 Tips
107+
* You can name attribute as you want, value of User Info is not changed (you can create attribute `myid` with User Info: `CloudCoreType: recordID`)
108+
* I recommend to mark *Record ID* attribute as `Indexed`, that can speed up updates in big databases.
109+
* *Record Data* attribute is used to store archived version of `CKRecord` with system fields only (like timestamps, tokens), so don't worry about size, no real data will be stored here.
110+
80111
## Example application
81112

82113
You can find example application at [Example](/Example/) directory.
@@ -100,4 +131,4 @@ You can find example application at [Example](/Example/) directory.
100131

101132
## Author
102133

103-
Vasily Ulianov, vasily@me.com
134+
Vasily Ulianov, [va...@me.com](http://www.google.com/recaptcha/mailhide/d?k=01eFEpy-HM-qd0Vf6QGABTjw==&c=JrKKY2bjm0Bp58w7zTvPiQ==)

Resources/Info-Mac.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0</string>
18+
<string>0.1.1</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Resources/Info-iOS.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0</string>
18+
<string>0.1.1</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Sources/Classes/CloudCore.swift

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,69 @@
99
import CoreData
1010
import CloudKit
1111

12+
/**
13+
Main framework class, in most cases you will use only methods from that class, all methods/properties are static.
14+
15+
## Save to cloud
16+
On application inialization call `observeCoreDataChanges` method, so framework will automatically monitor changes at Core Data and upload it to iCloud.
17+
18+
### Example
19+
20+
```swift
21+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
22+
// Register for push notifications about changes
23+
UIApplication.shared.registerForRemoteNotifications()
24+
// Enable uploading changed local data to CoreData
25+
CloudCore.observeCoreDataChanges(persistentContainer: self.persistentContainer, errorDelegate: nil)
26+
return true
27+
}
28+
```
29+
30+
## Fetch from cloud
31+
Updated objects from Core Data can be fetched with `CloudCore.fetchAndSave` methods. If you have called any of CloudCore methods before, CloudCore has automatically subscribed to hidden push notifications about data changes in CloudKit, so after you receive remoteNotifications about that changes, please call appropriate method to redirect that notification to CloudCore and framework will sync data for you.
32+
33+
If you want you can sync use force sync method.
34+
35+
Please use method with notification user info parameter if you're calling it from `didReceiveRemoteNotification`, because CloudCore extracts CloudKit database from notification to make less network requests on fetching.
36+
37+
### Example
38+
39+
```swift
40+
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
41+
if CloudCore.isCloudCoreNotification(withUserInfo: userInfo) {
42+
CloudCore.fetchAndSave(using: userInfo, container: self.persistentContainer, error: { error in
43+
NSLog("CloudKit fetch error: %@", error.localizedDescription)
44+
}, completion: { (fetchResult) in
45+
completionHandler(fetchResult.uiBackgroundFetchResult)
46+
})
47+
}
48+
}
49+
```
50+
*/
1251
open class CloudCore {
13-
public private(set) static var coreDataListener: CoreDataListener?
1452

53+
// MARK: Properties
54+
55+
private(set) static var coreDataListener: CoreDataListener?
56+
57+
/// CloudCore configuration, it's recommended to set up before calling any of CloudCore methods. You can read more at `CloudCoreConfig` struct description
1558
public static var config = CloudCoreConfig()
59+
60+
/// `Tokens` object, read more at class description. By default variable is loaded from User Defaults.
1661
public static var tokens = Tokens.loadFromUserDefaults()
1762

1863
public typealias NotificationUserInfo = [AnyHashable : Any]
1964

20-
/// Enable observing of changes at local database and saving them to iCloud
21-
/// - note: if that method was never called before it automaticly invokes `FetchAndSave` operation to load initial data from CloudKit
65+
// MARK: Save to cloud
66+
67+
/** Enable observing of changes at local database and saving them to iCloud
68+
69+
**Note**: If that method was never called before it automaticly invokes `fetchAndSave` method to load initial data from CloudKit
70+
71+
- Parameters:
72+
- persistentContainer: contextes without parents will be observed in that container, because saving of that context results writing information to disk or memory
73+
- errorDelegate: all errors that were occurred during upload processes will be reported to `errorDelegat`e and will contain `Error` or `CloudCoreError` objects.
74+
*/
2275
public static func observeCoreDataChanges(persistentContainer: NSPersistentContainer, errorDelegate: CloudCoreErrorDelegate?) {
2376
let errorBlock: ErrorBlock = { errorDelegate?.cloudCore(saveToCloudDidFailed: $0) }
2477

@@ -37,16 +90,18 @@ open class CloudCore {
3790
coreDataListener = nil
3891
}
3992

40-
/// Fetch changes from CloudKit database and save it to CoreData
41-
///
42-
/// - Parameters:
43-
/// - userInfo: notification's user info
44-
/// - error: block will be called every time when error occurs during process
45-
/// - completion: called after operation completion
46-
/// - fetchResult: `FetchResult` enumeration with results of operation. Can be converted to `UIBackgroundFetchResult` to use in background fetch completion calls.
47-
/// * .noData: if notification doesn't contain CloudCore's data, no fetching was done
48-
/// * .failed: if any errors have occured during process that status will be set
49-
/// * .newData: if data is fetched and saved successfully
93+
// MARK: Fetch from cloud
94+
95+
/** Fetch changes from one CloudKit database and save it to CoreData
96+
97+
Don't forget to check notification's userinfo by calling `isCloudCoreNotification(withUserInfo:)` before calling that method. If incorrect user info is provided `FetchResult.noData` will be returned at completion block.
98+
99+
- Parameters:
100+
- userInfo: notification's user info, CloudKit database will be extraced from that notification
101+
- container: `NSPersistentContainer` that will be used to save fetched data
102+
- error: block will be called every time when error occurs during process
103+
- completion: `FetchResult` enumeration with results of operation
104+
*/
50105
public static func fetchAndSave(using userInfo: NotificationUserInfo, container: NSPersistentContainer, error: ErrorBlock?, completion: @escaping (_ fetchResult: FetchResult) -> Void) {
51106
guard let cloudDatabase = self.database(for: userInfo) else {
52107
completion(.noData)
@@ -66,12 +121,14 @@ open class CloudCore {
66121
}
67122
}
68123
}
69-
70-
/// Fetch changes from all CloudKit databases and save it to Core Data
71-
///
72-
/// - Parameters:
73-
/// - error: block will be called every time when error occurs during process
74-
/// - completion: called when fetching and saving are completed
124+
125+
/** Fetch changes from all CloudKit databases and save it to Core Data
126+
127+
- Parameters:
128+
- container: `NSPersistentContainer` that will be used to save fetched data
129+
- error: block will be called every time when error occurs during process
130+
- completion: `FetchResult` enumeration with results of operation
131+
*/
75132
public static func fetchAndSave(container: NSPersistentContainer, error: ErrorBlock?, completion: (() -> Void)?) {
76133
DispatchQueue.global(qos: .utility).async {
77134
let operation = FetchAndSaveOperation(persistentContainer: container)
@@ -81,9 +138,11 @@ open class CloudCore {
81138
}
82139
}
83140

84-
/// Check if notification is CloudKit notification containing CloudCore data
85-
///
86-
/// - Parameter userInfo: userInfo of notification
141+
/** Check if notification is CloudKit notification containing CloudCore data
142+
143+
- Parameter userInfo: userInfo of notification
144+
- Returns: `true` if notification contains CloudCore data
145+
*/
87146
public static func isCloudCoreNotification(withUserInfo userInfo: NotificationUserInfo) -> Bool {
88147
return (database(for: userInfo) != nil)
89148
}
@@ -102,4 +161,3 @@ open class CloudCore {
102161
}
103162
}
104163
}
105-

Sources/Classes/Fetch/FetchAndSaveOperation.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import CloudKit
1010
import CoreData
1111

12+
/// An operation that fetches data from CloudKit and saves it to Core Data, you can use it without calling `CloudCore.fetchAndSave` methods if you application relies on `Operation`
1213
public class FetchAndSaveOperation: Operation {
1314

1415
private static let allDatabases = [
@@ -29,6 +30,12 @@ public class FetchAndSaveOperation: Operation {
2930
private let fetchOperationQueue = OperationQueue()
3031
private let coreDataOperationQueue = OperationQueue()
3132

33+
/// Initialize operation, it's recommended to set `errorBlock`
34+
///
35+
/// - Parameters:
36+
/// - databases: list of databases to fetch data from (now supported: private and shared)
37+
/// - persistentContainer: `NSPersistentContainer` that will be used to save data
38+
/// - tokens: previously saved `Tokens`, you can generate new ones if you want to fetch all data
3239
public init(from databases: [CKDatabase] = FetchAndSaveOperation.allDatabases, persistentContainer: NSPersistentContainer, tokens: Tokens = CloudCore.tokens) {
3340
self.tokens = tokens
3441
self.databases = databases
@@ -38,7 +45,8 @@ public class FetchAndSaveOperation: Operation {
3845
coreDataOperationQueue.name = "CloudCoreFetchFromCloud CoreData"
3946
coreDataOperationQueue.maxConcurrentOperationCount = 1
4047
}
41-
48+
49+
/// Performs the receiver’s non-concurrent task.
4250
override public func main() {
4351
if self.isCancelled { return }
4452

@@ -72,6 +80,7 @@ public class FetchAndSaveOperation: Operation {
7280
NotificationCenter.default.post(name: .CloudCoreDidSyncFromCloud, object: nil)
7381
}
7482

83+
/// Advises the operation object that it should stop executing its task.
7584
public override func cancel() {
7685
self.fetchOperationQueue.cancelAllOperations()
7786
self.coreDataOperationQueue.cancelAllOperations()

Sources/Classes/Fetch/PublicSubscriptions/PublicDatabaseSubscriptions.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,24 @@
88

99
import CloudKit
1010

11+
// TODO: Temporarily disabled, in development
12+
1113
/// Use that class to manage subscriptions to public CloudKit database.
1214
/// If you want to sync some records with public database you need to subsrcibe for notifications on that changes to enable iCloud -> Local database syncing.
13-
public class PublicDatabaseSubscriptions {
15+
class PublicDatabaseSubscriptions {
1416

1517
private static var userDefaultsKey: String { return CloudCore.config.userDefaultsKeyTokens }
1618
private static var prefix: String { return CloudCore.config.publicSubscriptionIDPrefix }
1719

18-
public internal(set) static var cachedIDs = UserDefaults.standard.stringArray(forKey: userDefaultsKey) ?? [String]()
20+
internal(set) static var cachedIDs = UserDefaults.standard.stringArray(forKey: userDefaultsKey) ?? [String]()
1921

2022
/// Create `CKQuerySubscription` for public database, use it if you want to enable syncing public iCloud -> Core Data
2123
///
2224
/// - Parameters:
2325
/// - recordType: The string that identifies the type of records to track. You are responsible for naming your app’s record types. This parameter must not be empty string.
2426
/// - predicate: The matching criteria to apply to the records. This parameter must not be nil. For information about the operators that are supported in search predicates, see the discussion in [CKQuery](apple-reference-documentation://hsDjQFvil9).
2527
/// - completion: returns subscriptionID and error upon operation completion
26-
public static func subscribe(recordType: String, predicate: NSPredicate, completion: ((_ subscriptionID: String, _ error: Error?) -> Void)?) {
28+
static func subscribe(recordType: String, predicate: NSPredicate, completion: ((_ subscriptionID: String, _ error: Error?) -> Void)?) {
2729
let id = prefix + UUID().uuidString
2830
let subscription = CKQuerySubscription(recordType: recordType, predicate: predicate, subscriptionID: id, options: [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion])
2931

@@ -50,7 +52,7 @@ public class PublicDatabaseSubscriptions {
5052
///
5153
/// - Parameters:
5254
/// - subscriptionID: id of subscription to remove
53-
public static func unsubscribe(subscriptionID: String, completion: ((Error?) -> Void)?) {
55+
static func unsubscribe(subscriptionID: String, completion: ((Error?) -> Void)?) {
5456
let operation = CKModifySubscriptionsOperation(subscriptionsToSave: [], subscriptionIDsToDelete: [subscriptionID])
5557
operation.modifySubscriptionsCompletionBlock = { _, _, error in
5658
if error == nil {
@@ -73,7 +75,7 @@ public class PublicDatabaseSubscriptions {
7375
/// Recommended to use after application's UserDefaults reset.
7476
///
7577
/// - Parameter completion: called upon operation completion, contains list of CloudCore subscriptions and error
76-
public static func refreshCache(errorCompletion: ErrorBlock? = nil, successCompletion: (([CKSubscription]) -> Void)? = nil) {
78+
static func refreshCache(errorCompletion: ErrorBlock? = nil, successCompletion: (([CKSubscription]) -> Void)? = nil) {
7779
let operation = FetchPublicSubscriptionsOperation()
7880
operation.errorBlock = errorCompletion
7981
operation.fetchCompletionBlock = { subscriptions in

Sources/Classes/Fetch/SubOperations/AsynchronousOperation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Foundation
1212
/// ## How to use:
1313
/// 1. Call `super.main()` when override `main` method, call `super.start()` when override `start` method.
1414
/// 2. When operation is finished or cancelled set `self.state = .finished`
15-
open class AsynchronousOperation: Operation {
15+
class AsynchronousOperation: Operation {
1616
open override var isAsynchronous: Bool { return true }
1717
open override var isExecuting: Bool { return state == .executing }
1818
open override var isFinished: Bool { return state == .finished }
@@ -28,14 +28,14 @@ open class AsynchronousOperation: Operation {
2828
}
2929
}
3030

31-
public enum State: String {
31+
enum State: String {
3232
case ready = "Ready"
3333
case executing = "Executing"
3434
case finished = "Finished"
3535
fileprivate var keyPath: String { return "is" + self.rawValue }
3636
}
3737

38-
open override func start() {
38+
override func start() {
3939
if self.isCancelled {
4040
state = .finished
4141
} else {
@@ -44,7 +44,7 @@ open class AsynchronousOperation: Operation {
4444
}
4545
}
4646

47-
open override func main() {
47+
override func main() {
4848
if self.isCancelled {
4949
state = .finished
5050
} else {

0 commit comments

Comments
 (0)