Skip to content

Commit

Permalink
Fixes 1.3.2 (#183)
Browse files Browse the repository at this point in the history
* Accept changes made by same device when fetching records

* Fix for NSSecureUnarchiveFromDataTransformer

* Bump version to 1.3.2
  • Loading branch information
mentrena authored Jun 25, 2022
1 parent 873dcbc commit 97dcd91
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.3.1</string>
<string>1.3.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
640C33EB22AD913300A85508 /* Array+OrderInsensitiveCompare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640C33EA22AD913300A85508 /* Array+OrderInsensitiveCompare.swift */; };
640C33ED22AD92CF00A85508 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640C33EC22AD92CF00A85508 /* TestError.swift */; };
6413ECF62559B36600BDC1DB /* RecordProcessingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6413ECF52559B36600BDC1DB /* RecordProcessingDelegate.swift */; };
6419F0C928664BBA00D84320 /* QSTestEntity3+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419F0C728664BBA00D84320 /* QSTestEntity3+CoreDataClass.swift */; };
6419F0CA28664BBA00D84320 /* QSTestEntity3+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419F0C828664BBA00D84320 /* QSTestEntity3+CoreDataProperties.swift */; };
6430AD6B26C1804500E8880D /* EncryptedModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6926C1804500E8880D /* EncryptedModel.xcdatamodeld */; };
6430AD6E26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6C26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift */; };
6430AD6F26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6D26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift */; };
Expand Down Expand Up @@ -116,6 +118,8 @@
640C33EA22AD913300A85508 /* Array+OrderInsensitiveCompare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+OrderInsensitiveCompare.swift"; sourceTree = "<group>"; };
640C33EC22AD92CF00A85508 /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
6413ECF52559B36600BDC1DB /* RecordProcessingDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordProcessingDelegate.swift; sourceTree = "<group>"; };
6419F0C728664BBA00D84320 /* QSTestEntity3+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QSTestEntity3+CoreDataClass.swift"; sourceTree = "<group>"; };
6419F0C828664BBA00D84320 /* QSTestEntity3+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QSTestEntity3+CoreDataProperties.swift"; sourceTree = "<group>"; };
6430AD6A26C1804500E8880D /* EncryptedModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = EncryptedModel.xcdatamodel; sourceTree = "<group>"; };
6430AD6C26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EntityWithEncryptedFields+CoreDataClass.swift"; sourceTree = "<group>"; };
6430AD6D26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EntityWithEncryptedFields+CoreDataProperties.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -327,6 +331,8 @@
64B46BE122B95C900017268F /* Transformable */ = {
isa = PBXGroup;
children = (
6419F0C728664BBA00D84320 /* QSTestEntity3+CoreDataClass.swift */,
6419F0C828664BBA00D84320 /* QSTestEntity3+CoreDataProperties.swift */,
64B46BF322B9629A0017268F /* QSTestEntity+CoreDataClass.swift */,
64B46BF422B9629A0017268F /* QSTestEntity+CoreDataProperties.swift */,
64B46BE822B95D570017268F /* QSNamesTransformer.swift */,
Expand Down Expand Up @@ -676,8 +682,10 @@
644164AF26B0383500ED033B /* MockCloudKitSynchronizerDelegate.swift in Sources */,
64CFAF2E22CD26E4009CF1F1 /* QSEmployee+CoreDataClass.swift in Sources */,
64B3A3A522B0293800E4E942 /* QSExample.xcdatamodeld in Sources */,
6419F0CA28664BBA00D84320 /* QSTestEntity3+CoreDataProperties.swift in Sources */,
64B3A38822B01C1700E4E942 /* DefaultCoreDataStackProviderTests.swift in Sources */,
64CFAF2F22CD26E4009CF1F1 /* QSEmployee+CoreDataProperties.swift in Sources */,
6419F0C928664BBA00D84320 /* QSTestEntity3+CoreDataClass.swift in Sources */,
64A452D9264C2A2300133F64 /* QSCompany_UUID+CoreDataProperties.swift in Sources */,
64A452E0264C2B1F00133F64 /* QSEmployee_Int+CoreDataClass.swift in Sources */,
64369248232EB25A004393B1 /* QSTestEntity2+CoreDataClass.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,47 @@ extension CoreDataAdapterTests {
let adapter = createAdapter()
let records = waitUntilSynced(adapter: adapter).updated
XCTAssertEqual(records.count, 1)
XCTAssertFalse(QSNamesTransformer.transformedValueCalled)
XCTAssertTrue(QSNamesTransformer.transformedValueCalled)
XCTAssertFalse(QSNamesTransformer.reverseTransformedValueCalled)
let transformed = QSNamesTransformer().transformedValue(["1", "2"]) as? NSData
XCTAssertEqual(records.first?["names"], transformed)
}

func testSaveChangesInRecords_transformableProperty_usesValueTransformer() {
QSNamesTransformer.register()
targetCoreDataStack = coreDataStack(modelName: "QSTransformableTestModel")
let adapter = createAdapter()
let record = CKRecord(recordType: "QSTestEntity", recordID: CKRecord.ID(recordName: "QSTestEntity.ent1"))
record["identifier"] = "ent1"
record["names"] = QSNamesTransformer().transformedValue(["1", "2", "3"]) as? CKRecordValueProtocol

QSNamesTransformer.resetValues()

waitUntilSynced(adapter: adapter, downloaded: [record])

let objects = try! targetCoreDataStack.managedObjectContext.executeFetchRequest(entityName: "QSTestEntity") as? [QSTestEntity]
XCTAssertEqual(objects?.count, 1)
let testEntity = objects?.first
XCTAssertEqual(testEntity?.identifier, "ent1")
XCTAssertEqual(testEntity?.names, ["1", "2", "3"])
XCTAssertTrue(QSNamesTransformer.reverseTransformedValueCalled)
XCTAssertFalse(QSNamesTransformer.transformedValueCalled)
}

func testRecordsToUploadWithLimit_secureTransformableProperty_usesValueTransformer() {
QSSecureNamesTransformer.register()
QSSecureNamesTransformer.resetValues()
targetCoreDataStack = coreDataStack(modelName: "QSTransformableTestModel")

insert(entityType: "QSTestEntity3",
properties: ["identifier": "identifier",
"names": ["1", "2"]],
context: targetCoreDataStack.managedObjectContext)
let adapter = createAdapter()
let records = waitUntilSynced(adapter: adapter).updated
XCTAssertEqual(records.count, 1)
XCTAssertFalse(QSSecureNamesTransformer.transformedValueCalled)
XCTAssertTrue(QSSecureNamesTransformer.reverseTransformedValueCalled)

let record = records.first!
guard let namesData = record["names"] as? Data else {
Expand All @@ -825,26 +864,26 @@ extension CoreDataAdapterTests {
let names = (try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: namesData)) as? [String]
XCTAssertEqual(names, ["1", "2"])
}
func testSaveChangesInRecords_transformableProperty_usesValueTransformer() {
QSNamesTransformer.register()

func testSaveChangesInRecords_secureTransformableProperty_usesValueTransformer() {
QSSecureNamesTransformer.register()
targetCoreDataStack = coreDataStack(modelName: "QSTransformableTestModel")
let adapter = createAdapter()
let record = CKRecord(recordType: "QSTestEntity", recordID: CKRecord.ID(recordName: "QSTestEntity.ent1"))
let record = CKRecord(recordType: "QSTestEntity3", recordID: CKRecord.ID(recordName: "QSTestEntity3.ent1"))
record["identifier"] = "ent1"
record["names"] = QSNamesTransformer().reverseTransformedValue(["1", "2", "3"]) as? CKRecordValueProtocol
QSNamesTransformer.resetValues()
record["names"] = QSSecureNamesTransformer().reverseTransformedValue(["1", "2", "3"]) as? CKRecordValueProtocol

QSSecureNamesTransformer.resetValues()

waitUntilSynced(adapter: adapter, downloaded: [record])
let objects = try! targetCoreDataStack.managedObjectContext.executeFetchRequest(entityName: "QSTestEntity") as? [QSTestEntity]

let objects = try! targetCoreDataStack.managedObjectContext.executeFetchRequest(entityName: "QSTestEntity3") as? [QSTestEntity3]
XCTAssertEqual(objects?.count, 1)
let testEntity = objects?.first
XCTAssertEqual(testEntity?.identifier, "ent1")
XCTAssertEqual(testEntity?.names, ["1", "2", "3"])
XCTAssertFalse(QSNamesTransformer.reverseTransformedValueCalled)
XCTAssertTrue(QSNamesTransformer.transformedValueCalled)
XCTAssertFalse(QSSecureNamesTransformer.reverseTransformedValueCalled)
XCTAssertTrue(QSSecureNamesTransformer.transformedValueCalled)
}

func testRecordsToUploadWithLimit_transformablePropertyNoValueTransformer_usesKeyedArchiver() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,49 @@ class QSNamesTransformer: ValueTransformer {

override func transformedValue(_ value: Any?) -> Any? {
QSNamesTransformer.transformedValueCalled = true
guard let data = value as? Data else {
return nil
}
return NSKeyedUnarchiver.unarchiveObject(with: data)
return NSKeyedArchiver.archivedData(withRootObject: value)
}

override func reverseTransformedValue(_ value: Any?) -> Any? {
QSNamesTransformer.reverseTransformedValueCalled = true
return NSKeyedUnarchiver.unarchiveObject(with: value as! Data)
}
}

class QSSecureNamesTransformer: NSSecureUnarchiveFromDataTransformer {

static var transformedValueCalled = false
static var reverseTransformedValueCalled = false
static func resetValues() {
transformedValueCalled = false
reverseTransformedValueCalled = false
}

static func register() {
ValueTransformer.setValueTransformer(QSSecureNamesTransformer(), forName: .secureNamesTransformerName)
}

override class func transformedValueClass() -> AnyClass {
NSData.self
}

override class func allowsReverseTransformation() -> Bool {
true
}

override func transformedValue(_ value: Any?) -> Any? {
QSSecureNamesTransformer.transformedValueCalled = true
return NSKeyedUnarchiver.unarchiveObject(with: value as! Data)

}

override func reverseTransformedValue(_ value: Any?) -> Any? {
QSSecureNamesTransformer.reverseTransformedValueCalled = true
return NSKeyedArchiver.archivedData(withRootObject: value)
}
}

extension NSValueTransformerName {
static let namesTransformerName = NSValueTransformerName(rawValue: "QSNamesTransformer")
static let secureNamesTransformerName = NSValueTransformerName(rawValue: "QSSecureNamesTransformer")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// QSTestEntity3+CoreDataClass.swift
// SyncKitCoreDataExampleTests
//
// Created by Manuel on 24/06/2022.
// Copyright © 2022 Manuel Entrena. All rights reserved.
//
//

import Foundation
import CoreData
import SyncKit

@objc(QSTestEntity3)
public class QSTestEntity3: NSManagedObject, PrimaryKey {
public static func primaryKey() -> String {
return "identifier"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// QSTestEntity3+CoreDataProperties.swift
// SyncKitCoreDataExampleTests
//
// Created by Manuel on 24/06/2022.
// Copyright © 2022 Manuel Entrena. All rights reserved.
//
//

import Foundation
import CoreData


extension QSTestEntity3 {

@nonobjc public class func fetchRequest() -> NSFetchRequest<QSTestEntity3> {
return NSFetchRequest<QSTestEntity3>(entityName: "QSTestEntity3")
}

@NSManaged public var identifier: String?
@NSManaged public var names: NSArray?

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14490.98" systemVersion="18G87" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="QSTestEntity" representedClassName=".QSTestEntity" syncable="YES" codeGenerationType="class">
<attribute name="identifier" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="names" optional="YES" attributeType="Transformable" valueTransformerName="QSNamesTransformer" customClassName=".NSArray" syncable="YES"/>
<attribute name="identifier" optional="YES" attributeType="String"/>
<attribute name="names" optional="YES" attributeType="Transformable" valueTransformerName="QSNamesTransformer" customClassName=".NSArray"/>
</entity>
<entity name="QSTestEntity2" representedClassName="QSTestEntity2" syncable="YES" codeGenerationType="class">
<attribute name="identifier" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="names" optional="YES" attributeType="Transformable" customClassName="NSArray" syncable="YES"/>
<attribute name="identifier" optional="YES" attributeType="String"/>
<attribute name="names" optional="YES" attributeType="Transformable" customClassName="NSArray"/>
</entity>
<entity name="QSTestEntity3" representedClassName="QSTestEntity3" syncable="YES" codeGenerationType="class">
<attribute name="identifier" optional="YES" attributeType="String"/>
<attribute name="names" optional="YES" attributeType="Transformable" valueTransformerName="QSSecureNamesTransformer" customClassName=".NSArray"/>
</entity>
<elements>
<element name="QSTestEntity" positionX="-54" positionY="9" width="128" height="75"/>
<element name="QSTestEntity2" positionX="-54" positionY="27" width="128" height="75"/>
<element name="QSTestEntity3" positionX="-54" positionY="45" width="128" height="59"/>
</elements>
</model>
2 changes: 1 addition & 1 deletion Example/Realm/SyncKitRealm/SyncKitRealm/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.3.1</string>
<string>1.3.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.3.1</string>
<string>1.3.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
2 changes: 1 addition & 1 deletion SyncKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'SyncKit'
s.version = '1.3.1'
s.version = '1.3.2'
s.summary = 'CloudKit synchronization for your Core Data or Realm model.'

s.description = <<-DESC
Expand Down
39 changes: 29 additions & 10 deletions SyncKit/Classes/CoreData/CoreDataAdapter+Private.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,36 @@ extension CoreDataAdapter {
func transformedValue(_ value: Any, valueTransformerName: String?) -> Any? {
if let valueTransformerName = valueTransformerName {
let transformer = ValueTransformer(forName: NSValueTransformerName(valueTransformerName))
return transformer?.transformedValue(value)
} else if let data = value as? Data {
return QSCoder.shared.object(from: data)
if #available(iOS 12.0, OSX 10.14, watchOS 5.0, *) {
if let secureUnarchiveTransformer = transformer as? NSSecureUnarchiveFromDataTransformer {
return secureUnarchiveTransformer.reverseTransformedValue(value)
} else {
return transformer?.transformedValue(value)
}
} else {
return transformer?.transformedValue(value)
}
} else {
return nil
return QSCoder.shared.data(from: value)
}
}

func reverseTransformedValue(_ value: Any, valueTransformerName: String?) -> Any? {
if let valueTransformerName = valueTransformerName {
let transformer = ValueTransformer(forName: NSValueTransformerName(valueTransformerName))
return transformer?.reverseTransformedValue(value)
if #available(iOS 12.0, OSX 10.14, watchOS 5.0, *) {
if let secureUnarchiveTransformer = transformer as? NSSecureUnarchiveFromDataTransformer {
return secureUnarchiveTransformer.transformedValue(value)
} else {
return transformer?.reverseTransformedValue(value)
}
} else {
return transformer?.reverseTransformedValue(value)
}
} else if let data = value as? Data {
return QSCoder.shared.object(from: data)
} else {
return QSCoder.shared.data(from: value)
return nil
}
}

Expand Down Expand Up @@ -357,8 +373,11 @@ extension CoreDataAdapter {
let asset = CKAsset(fileURL: fileURL)
record[attributeName] = asset
} else if attributeDescription.attributeType == .transformableAttributeType,
let value = value,
let transformed = self.reverseTransformedValue(value, valueTransformerName: attributeDescription.valueTransformerName) as? CKRecordValueProtocol{
let value = value,
let transformed = self.transformedValue(
value,
valueTransformerName: attributeDescription.valueTransformerName
) as? CKRecordValueProtocol {
record[attributeName] = transformed
} else if let encrypted = encryptedFields,
encrypted.contains(attributeName) {
Expand Down Expand Up @@ -534,8 +553,8 @@ extension CoreDataAdapter {
object.setValue(data, forKey: attributeName)
} else if let value = value,
attributeDescription.attributeType == .transformableAttributeType {
object.setValue(transformedValue(value,
valueTransformerName: attributeDescription.valueTransformerName),
object.setValue(reverseTransformedValue(value,
valueTransformerName: attributeDescription.valueTransformerName),
forKey: attributeName)
} else {
object.setValue(value, forKey: attributeName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,13 @@ extension CloudKitSynchronizer {

func fetchZoneChanges(_ zoneIDs: [CKRecordZone.ID], completion: @escaping (Error?)->()) {

let operation = FetchZoneChangesOperation(database: database, zoneIDs: zoneIDs, zoneChangeTokens: activeZoneTokens, modelVersion: compatibilityVersion, ignoreDeviceIdentifier: deviceIdentifier, desiredKeys: nil) { (zoneResults) in
let operation = FetchZoneChangesOperation(
database: database,
zoneIDs: zoneIDs,
zoneChangeTokens: activeZoneTokens,
modelVersion: compatibilityVersion,
ignoreDeviceIdentifier: nil,
desiredKeys: nil) { (zoneResults) in

self.dispatchQueue.async {
var pendingZones = [CKRecordZone.ID]()
Expand Down

0 comments on commit 97dcd91

Please sign in to comment.