core data migration

This commit is contained in:
Chris Li 2016-09-22 14:10:48 -04:00
parent 8c7141854f
commit 257943b3a5
17 changed files with 67 additions and 351 deletions

View File

@ -94,14 +94,15 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp
cellTitles.append([])
}
if let isLocal = book.isLocal?.boolValue {
if isLocal {
cellTitles[1] = [LocalizedStrings.remove]
} else {
cellTitles[1] = book.spaceState == .NotEnough ? [LocalizedStrings.spaceNotEnough] : [LocalizedStrings.download]
}
} else {
switch book.state {
case .Cloud:
cellTitles[1] = book.spaceState == .NotEnough ? [LocalizedStrings.spaceNotEnough] : [LocalizedStrings.download]
case .Downloading:
cellTitles[1] = [LocalizedStrings.downloading]
case .Local:
cellTitles[1] = [LocalizedStrings.remove]
default:
break
}
}

View File

@ -336,7 +336,7 @@ class CloudBooksController: UITableViewController, NSFetchedResultsControllerDel
}
private var onlineCompoundPredicate: NSCompoundPredicate {
let isCloudPredicate = NSPredicate(format: "isLocal == false")
let isCloudPredicate = NSPredicate(format: "stateRaw == 0")
return NSCompoundPredicate(andPredicateWithSubpredicates: [langPredicate, isCloudPredicate])
}

View File

@ -221,7 +221,7 @@ class DownloadTasksController: UITableViewController, NSFetchedResultsController
// Remove resume data
// Delete downloadTask object and set book to not local
downloadTask.book?.removeResumeData()
downloadTask.book?.isLocal = NSNumber(bool: false)
downloadTask.book?.state = .Cloud
self.managedObjectContext.deleteObject(downloadTask)
}
} else {

View File

@ -146,7 +146,7 @@ class LocalBooksController: UITableViewController, NSFetchedResultsControllerDel
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
fetchRequest.predicate = NSPredicate(format: "isLocal == true")
fetchRequest.predicate = NSPredicate(format: "stateRaw >= 2")
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "language.name", cacheName: "LocalFRC" + NSBundle.buildVersion)
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)

View File

@ -49,7 +49,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.8.1047</string>
<string>1.8.1070</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View File

@ -21,7 +21,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.8.1051</string>
<string>1.8.1074</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>

View File

@ -81,13 +81,13 @@
9764F5931D830EF200E0B1C4 /* liblzma.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9764F5921D830EF200E0B1C4 /* liblzma.tbd */; };
9764F5971D8339D500E0B1C4 /* JSInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9764F5961D8339D500E0B1C4 /* JSInjection.swift */; };
9764F5991D833F2B00E0B1C4 /* KiwixURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9764F5981D833F2B00E0B1C4 /* KiwixURL.swift */; };
9764F59F1D83553F00E0B1C4 /* 1.8.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 9764F59E1D83553F00E0B1C4 /* 1.8.xcmappingmodel */; };
9779C3141D4575AD0064CC8E /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97E609F01D103DED00EBCB9D /* NotificationCenter.framework */; };
9779C3171D4575AE0064CC8E /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779C3161D4575AE0064CC8E /* TodayViewController.swift */; };
9779C31A1D4575AE0064CC8E /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9779C3181D4575AE0064CC8E /* MainInterface.storyboard */; };
9779C31E1D4575AE0064CC8E /* Bookmarks.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9779C3131D4575AD0064CC8E /* Bookmarks.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
9787BC211D9318300030D311 /* WelcomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9787BC201D9318300030D311 /* WelcomeController.swift */; };
9787BC231D9318570030D311 /* TableOfContentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9787BC221D9318570030D311 /* TableOfContentsController.swift */; };
9787BC271D944E890030D311 /* 1.8.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 9787BC261D944E890030D311 /* 1.8.xcmappingmodel */; };
979C518D1CECAE4C001707F2 /* PreferenceWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979C518B1CECAE4C001707F2 /* PreferenceWindowController.swift */; };
979CB60F1D04AD04005E1BA1 /* PreferenceTabController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB60E1D04AD04005E1BA1 /* PreferenceTabController.swift */; };
979CB6C81D05CF37005E1BA1 /* SearchResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6C71D05CF37005E1BA1 /* SearchResultController.swift */; };
@ -302,7 +302,6 @@
9764F5921D830EF200E0B1C4 /* liblzma.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.tbd; path = usr/lib/liblzma.tbd; sourceTree = SDKROOT; };
9764F5961D8339D500E0B1C4 /* JSInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSInjection.swift; path = Main/JSInjection.swift; sourceTree = "<group>"; };
9764F5981D833F2B00E0B1C4 /* KiwixURL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KiwixURL.swift; sourceTree = "<group>"; };
9764F59E1D83553F00E0B1C4 /* 1.8.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; name = 1.8.xcmappingmodel; path = Kiwix/CoreData/Migration/1.8.xcmappingmodel; sourceTree = SOURCE_ROOT; };
976A0C801D41619C0006A742 /* DZNEmptyDataSet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DZNEmptyDataSet.framework; path = "../../../../Users/chrisli/Library/Developer/Xcode/DerivedData/Kiwix-ayxrfhaqnfxzendihdolvkklkmhk/Build/Products/Debug-iphoneos/DZNEmptyDataSet/DZNEmptyDataSet.framework"; sourceTree = "<group>"; };
9779C3131D4575AD0064CC8E /* Bookmarks.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Bookmarks.appex; sourceTree = BUILT_PRODUCTS_DIR; };
9779C3161D4575AE0064CC8E /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = "<group>"; };
@ -310,6 +309,7 @@
9779C31B1D4575AE0064CC8E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9787BC201D9318300030D311 /* WelcomeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WelcomeController.swift; path = Others/WelcomeController.swift; sourceTree = "<group>"; };
9787BC221D9318570030D311 /* TableOfContentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsController.swift; path = Others/TableOfContentsController.swift; sourceTree = "<group>"; };
9787BC261D944E890030D311 /* 1.8.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; name = 1.8.xcmappingmodel; path = Kiwix/CoreData/Migration/1.8.xcmappingmodel; sourceTree = SOURCE_ROOT; };
979C518B1CECAE4C001707F2 /* PreferenceWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferenceWindowController.swift; path = "Kiwix-OSX/Controllers/PreferenceWindowController.swift"; sourceTree = SOURCE_ROOT; };
979CB60E1D04AD04005E1BA1 /* PreferenceTabController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferenceTabController.swift; path = Controllers/PreferenceTabController.swift; sourceTree = "<group>"; };
979CB6C71D05CF37005E1BA1 /* SearchResultController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchResultController.swift; path = Controllers/SearchResultController.swift; sourceTree = "<group>"; };
@ -545,7 +545,7 @@
children = (
97D6812F1D6F70EC00E5FA99 /* 1.5.xcmappingmodel */,
97D681301D6F70EC00E5FA99 /* MigrationPolicy.swift */,
9764F59E1D83553F00E0B1C4 /* 1.8.xcmappingmodel */,
9787BC261D944E890030D311 /* 1.8.xcmappingmodel */,
);
name = Migration;
path = Kiwix;
@ -1542,7 +1542,7 @@
97D681231D6F70AC00E5FA99 /* GlobalQueue.swift in Sources */,
97A1FD3B1D6F724E00A80EE2 /* stringTools.cpp in Sources */,
97A1FD321D6F723D00A80EE2 /* resourceTools.cpp in Sources */,
9764F59F1D83553F00E0B1C4 /* 1.8.xcmappingmodel in Sources */,
9787BC271D944E890030D311 /* 1.8.xcmappingmodel in Sources */,
971A10321D022AD5007FC62C /* SearchBar.swift in Sources */,
97E60A061D10504000EBCB9D /* LibraryBackupTBVC.swift in Sources */,
97A1FD451D6F728200A80EE2 /* StringTools.swift in Sources */,

View File

@ -25,13 +25,13 @@
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Kiwix/Operations/GlobalQueue.swift"
timestampString = "496186011.525439"
filePath = "Kiwix/CoreData/Migration/MigrationPolicy.swift"
timestampString = "496258633.225782"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "44"
endingLineNumber = "44"
landmarkName = "add(load:)"
startingLineNumber = "20"
endingLineNumber = "20"
landmarkName = "bookState(bool:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>

View File

@ -25,11 +25,11 @@ extension Book {
@NSManaged var hasPic: Bool
@NSManaged var id: String
@NSManaged var includeInSearch: Bool
@NSManaged var isLocal: NSNumber?
@NSManaged var mediaCount: Int64
@NSManaged var meta4URL: String?
@NSManaged var pid: String?
@NSManaged var publisher: String?
@NSManaged var stateRaw: Int16
@NSManaged var title: String?
@NSManaged var articles: Set<Article>

View File

@ -112,7 +112,7 @@ class Book: NSManagedObject {
class func fetchLocal(context: NSManagedObjectContext) -> [ZimID: Book] {
let fetchRequest = NSFetchRequest(entityName: "Book")
let predicate = NSPredicate(format: "isLocal = true")
let predicate = NSPredicate(format: "stateRaw == 2")
fetchRequest.predicate = predicate
let localBooks = fetch(fetchRequest, type: Book.self, context: context) ?? [Book]()
@ -218,6 +218,22 @@ class Book: NSManagedObject {
// MARK: - States
var state: BookState {
get {
switch stateRaw {
case 0: return .Cloud
case 1: return .Downloading
case 2: return .Local
case 3: return .Retained
case 4: return .Purgeable
default: return .Cloud
}
}
set {
stateRaw = Int16(newValue.rawValue)
}
}
var spaceState: BookSpaceState {
guard let freeSpaceInBytes = UIDevice.availableDiskSpace?.freeSize else {return .Enough}
if (0.8 * Double(freeSpaceInBytes)) > Double(fileSize) {
@ -230,6 +246,11 @@ class Book: NSManagedObject {
}
}
enum BookState: Int {
case Cloud, Downloading, Local, Retained, Purgeable
}
enum BookSpaceState: Int {
case Enough, Caution, NotEnough
}

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11232" systemVersion="16A319" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.8">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11232" systemVersion="16A323" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.8">
<entity name="Article" representedClassName=".Article" syncable="YES">
<attribute name="bookmarkDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="isBookmarked" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="isMainPage" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="lastPosition" optional="YES" attributeType="Double" usesScalarValueType="YES" syncable="YES"/>
<attribute name="lastReadDate" optional="YES" attributeType="Date" usesScalarValueType="NO" indexed="YES" syncable="YES"/>
<attribute name="needsUpdate" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="snippet" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="thumbImageURL" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" attributeType="String" syncable="YES"/>
@ -35,11 +36,11 @@
<attribute name="hasPic" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="includeInSearch" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="isLocal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
<attribute name="mediaCount" optional="YES" attributeType="Integer 64" minValueString="0" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="meta4URL" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="pid" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="publisher" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="stateRaw" attributeType="Integer 16" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" indexed="YES" syncable="YES"/>
<relationship name="articles" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Article" inverseName="book" inverseEntity="Article" syncable="YES"/>
<relationship name="downloadTask" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DownloadTask" inverseName="book" inverseEntity="DownloadTask" syncable="YES"/>
@ -90,7 +91,7 @@
<relationship name="articles" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Article" inverseName="tags" inverseEntity="Article" syncable="YES"/>
</entity>
<elements>
<element name="Article" positionX="-657" positionY="-153" width="128" height="208"/>
<element name="Article" positionX="-657" positionY="-153" width="128" height="225"/>
<element name="Book" positionX="-947" positionY="-142" width="128" height="345"/>
<element name="DownloadTask" positionX="-657" positionY="90" width="128" height="105"/>
<element name="Language" positionX="-657" positionY="216" width="128" height="103"/>

File diff suppressed because one or more lines are too long

View File

@ -14,3 +14,13 @@ class MigrationPolicy1_5: NSEntityMigrationPolicy {
return !bool
}
}
class MigrationPolicy1_8: NSEntityMigrationPolicy {
func bookState(bool: NSNumber?) -> NSNumber {
if let bool = bool?.boolValue {
return bool ? NSNumber(integer: 2) : NSNumber(integer: 0)
} else {
return NSNumber(integer: 1)
}
}
}

View File

@ -31,7 +31,7 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation {
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context),
let downloadTask = DownloadTask.addOrUpdate(book, context: context) else {return}
book.isLocal = nil
book.state = .Downloading
downloadTask.state = .Queued
// Overwrite progress
@ -75,14 +75,14 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation {
context.performBlockAndWait({
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context) else {return}
book.isLocal = nil
book.state = .Downloading
})
} else {
let context = NSManagedObjectContext.mainQueueContext
context.performBlockAndWait({
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context) else {return}
book.isLocal = false
book.state = .Cloud
guard let downloadTask = book.downloadTask else {return}
context.deleteObject(downloadTask)

View File

@ -114,7 +114,7 @@ private class Process: Operation, NSXMLParserDelegate, AutomaticInjectionOperati
guard let book = Book.fetch(id, context: self.context) else {return}
// Delete Book object only if book is online, i.e., is not associated with a download task or is not local
guard book.isLocal == false else {return}
guard book.state == .Cloud else {return}
self.context.deleteObject(book)
self.hasUpdate = true
})

View File

@ -74,7 +74,7 @@ class ScanLocalBookOperation: Operation {
for id in removedZimFileIDs {
guard let book = localBooks[id] else {continue}
if let _ = book.meta4URL {
book.isLocal = false
book.state = .Cloud
} else {
context.deleteObject(book)
}
@ -86,7 +86,7 @@ class ScanLocalBookOperation: Operation {
let book = Book.fetch(id, context: NSManagedObjectContext.mainQueueContext)
return book ?? Book.add(reader.metaData, context: NSManagedObjectContext.mainQueueContext)
}() else {return}
book.isLocal = true
book.state = .Local
book.hasIndex = reader.hasIndex()
book.hasPic = !reader.fileURL.absoluteString!.containsString("nopic")
if let downloadTask = book.downloadTask {context.deleteObject(downloadTask)}

View File

@ -88,7 +88,8 @@ class ZimMultiReader: NSObject, DirectoryMonitorDelegate {
// MARK: - Loading System
func data(id: String, contentURLString: String) -> [String: AnyObject]? {
func data(host: String, contentURLString: String) -> [String: AnyObject]? {
let id = pidMap[host] ?? host
guard let reader = readers[id] else {return nil}
return reader.dataWithContentURLString(contentURLString) as? [String: AnyObject]
}