TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Jul 2017 |
SwiftSwift Version | 3.0 |
SPMSupports SPM | ✓ |
Maintained by incetro.
Tracing is a framework written in Swift that makes it easy for you to divide your cache (CoreData, Realm, etc.) and UI layer. The idea is based on the great article. As you can read in this article, you can use this framework for easy updates your UITableView or something else.
In this case we are using built-in mapper. For this your plain objects must support protocol Mappable
. So, let’s create some models:
import Tracing
// MARK: - Storable
protocol Storable: class {
static var entityName: String { get }
}
// MARK: - NSManagedObject
extension Storable where Self: NSManagedObject {
static var entityName: String {
return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
}
init(in context: NSManagedObjectContext) {
guard let entity = NSEntityDescription.entity(forEntityName: Self.entityName, in: context) else {
fatalError("Cannot create entity for entity name: \(Self.entityName)")
}
self.init(entity: entity, insertInto: context)
}
}
// MARK: - CategoryModelObject
class CategoryModelObject: NSManagedObject, Storable {
@NSManaged var id: Int64
@NSManaged var name: String
@NSManaged var positions: NSSet
}
// MARK: - PositionModelObject
class PositionModelObject: NSManagedObject, Storable {
@NSManaged var id: Int64
@NSManaged var name: String
@NSManaged var price: Double
@NSManaged var category: CategoryModelObject?
}
// MARK: - CategoryPlainObject
class CategoryPlainObject: Mappable {
let id: Int64
let name: String
var positions: [PositionPlainObject] = [] // Array of objects
required init(with resolver: Resolver) throws {
self.id = try resolver.value("id")
self.name = try resolver.value("name")
self.positions = (try? resolver.value("positions")) ?? []
}
}
// MARK: - CategoryPlainObject
class PositionPlainObject: Mappable {
let id: Int64
let name: String
let price: Double
var category: CategoryPlainObject? = nil // Nested object
required init(with resolver: Resolver) throws {
self.id = try resolver.value("id")
self.name = try resolver.value("name")
self.price = try resolver.value("price")
self.category = try? resolver.value("category")
}
}
Okay, we have 2 model object classes (CategoryModelObject, PositionModelObject) and 2 plain object classes (CategoryPlainObject, PositionPlainObject). Now you can use Tracing:
/// Create request for cache
let request = CacheRequest(entityName: CategoryModelObject.entityName)
/// Create tracker that tracks changes with CategoryModelObject (Your NSManagedObject)
/// and notify you about this with CategoryPlainObject (Your plain object)
let tracker = Tracing.coreTracker(cacheType: CategoryModelObject.self, plainType: CategoryPlainObject.self)
/// Setup the tracker
tracker.setup(with: request, context: context) { transactionBatch in
/// Transactions with inserted objects
transactionBatch.insertedTransactions
/// Transactions with updated objects
transactionBatch.updatedTransactions
/// Transactions with removed objects
transactionBatch.removedTransactions
/// Transactions with moved objects
transactionBatch.movedTansactions
/// Checking for empty
transactionBatch.isEmpty
/// Or you can get inserted/updated/removed/moved objects
for category in transactionBatch.insertedObjects {
/// CategoryPlainObject
category
/// Use category's simple properties
category.id
category.name
/// And nested objects
category.positions
}
}
class CategoryMapper: CacheMapper {
typealias CacheType = CategoryModelObject
typealias PlainType = CategoryPlainObject
func map(from cacheObject: CategoryModelObject) throws -> CategoryPlainObject {
/// Map CategoryModelObject to CategoryPlainObject
}
}
/// And use it
let mapper = CategoryMapper()
let tracker = Tracing.coreTracker(with: mapper)
let name = CategoryModelObject.entityName
let filter = "id > 5"
let sortDesriptor = SortDescriptor(withKey: "name", ascending: true)
let request = CacheRequest(entityName: name, predicate: filter, sortDescriptors: [sortDesriptor])
If you prefer not to use any dependency managers, you can integrate Tracing into your project manually.
Open up Terminal, cd
into your top-level project directory, and run the following command “if” your project is not initialized as a git repository:
$ git init
Add Tracing as a git submodule by running the following command:
$ git submodule add https://github.com/incetro/Tracing.git
Open the new Tracing
folder, and drag the Tracing.xcodeproj
into the Project Navigator of your application’s Xcode project.
It should appear nested underneath your application’s blue project icon. Whether it is above or below all the other Xcode groups does not matter.
Select the Tracing.xcodeproj
in the Project Navigator and verify the deployment target matches that of your application target.
Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the “Targets” heading in the sidebar.
In the tab bar at the top of that window, open the “General” panel.
Click on the +
button under the “Embedded Binaries” section.
You will see two different Tracing.xcodeproj
folders each with two different versions of the Tracing.framework
nested inside a Products
folder.
It does not matter which
Products
folder you choose from, but it does matter whether you choose the top or bottomTracing.framework
.
Select the top Tracing.framework
for iOS and the bottom one for OS X.
You can verify which one you selected by inspecting the build log for your project. The build target for
Tracing
will be listed as eitherTracing iOS
,Tracing macOS
,Tracing tvOS
orTracing watchOS
.
And that’s it!
The
Tracing.framework
is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.
incetro, [email protected]
Tracing is available under the MIT license. See the LICENSE file for more info.