· 7 years ago · Jan 23, 2019, 05:14 AM
1import Foundation
2import RealmSwift
3import SQLite3
4
5class ExportDB {
6
7 private var realm: Realm
8 private var db: OpaquePointer?
9
10 internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
11 internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
12
13 init(realm: Realm) {
14 self.realm = realm
15 }
16
17 public func export() {
18 let fileURL = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
19 .appendingPathComponent("temp.sqlite")
20
21 do { try FileManager.default.removeItem(at: fileURL) }
22 catch { debugPrint(#function, error.localizedDescription) }
23
24 if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
25 print("error opening database")
26 }
27
28 let start = CFAbsoluteTimeGetCurrent()
29 realm.schema.objectSchema.forEach { (schema) in
30 exportTable(schema: schema)
31 }
32 let diff = CFAbsoluteTimeGetCurrent() - start
33 print("Took \(diff) seconds to Create Table")
34
35
36 let start1 = CFAbsoluteTimeGetCurrent()
37 realm.schema.objectSchema.forEach { (schema) in
38 fillTable(schema: schema)
39 }
40 let diff1 = CFAbsoluteTimeGetCurrent() - start1
41 print("Took \(diff1) seconds to Export Values")
42
43 }
44
45 private func getSQLType(_ property: (Property)) -> String {
46 switch property.type {
47 case .bool: return "BOOL"
48 case .int: return "INTEGER"
49 case .string, .date: return "TEXT"
50 case .data: return "BLOB"
51 case .float, .double: return "REAL"
52 default: return "TEXT"
53 }
54 }
55
56 private func exportTable(schema: ObjectSchema) {
57 var sql = "CREATE TABLE IF NOT EXISTS \(schema.className) ("
58 let primaryKeyProperty = schema.primaryKeyProperty
59 schema.properties.forEach { (property) in
60 if property.type == .linkingObjects || property.isArray || property.type == .object || property.type == .data { return }
61
62 sql.append("\'\(property.name)\'")
63
64 sql.append(" \(getSQLType(property))")
65
66 if property == primaryKeyProperty { sql.append(" PRIMARY KEY") }
67
68 if !property.isOptional { sql.append(" NOT NULL") }
69
70 sql.append(", ")
71 }
72
73 sql.removeLast()
74 sql.removeLast()
75 sql.append(")")
76
77 if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
78 let errmsg = String(cString: sqlite3_errmsg(db)!)
79 debugPrint(schema.className, "error creating table: \(errmsg)")
80 }
81 }
82
83 private func fillTable(schema: ObjectSchema) {
84 if let classObject = NSClassFromString("ChecklistFacil.\(schema.className)") {
85 let objects = realm.objects(classObject as! Object.Type)
86 if objects.isEmpty { return }
87
88 var sql = "INSERT INTO \(schema.className) VALUES ("
89
90 objects.forEach { (object) in
91 schema.properties.forEach { (property) in
92 if property.type == .linkingObjects || property.isArray || property.type == .object || property.type == .data { return }
93
94 if let value = object.value(forKey: property.name) {
95 if getSQLType(property) == "TEXT" {
96 if property.type == .string {
97 var textValue = String(format: "%@", value as! CVarArg)
98 textValue = textValue.replacingOccurrences(of: "\"", with: "\u{0022}")
99 textValue = textValue.replacingOccurrences(of: "\'", with: "\u{FF07}")
100 sql.append("\'\(textValue)\', ")
101 } else { sql.append("\'\(value)\', ") }
102
103 } else { sql.append("\(value), ") }
104
105 } else { sql.append("NULL, ") }
106 }
107
108 sql.removeLast()
109 sql.removeLast()
110 sql.append("),\n(")
111 }
112
113 sql.removeLast()
114 sql.removeLast()
115 sql.removeLast()
116
117 if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
118 let errmsg = String(cString: sqlite3_errmsg(db)!)
119 debugPrint(schema.className, "error inserting in table: \(errmsg)")
120 }
121 }
122 }
123}