• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

main code repository.


Commit MetaInfo

Revision56d226ff8dd05b4a50b9e2c8d5a22b13b8c8249b (tree)
Time2018-03-18 10:40:33
Authormasakih <masakih@user...>
Commitermasakih

Log Message

CoreDataのスレッドを使用するように修正

Change Summary

Incremental Difference

--- a/KCD/AirCorpsChangeNameCommand.swift
+++ b/KCD/AirCorpsChangeNameCommand.swift
@@ -22,7 +22,8 @@ final class AirCorpsChangeNameCommand: JSONCommand {
2222 guard let name = parameter["api_name"].string else { return }
2323
2424 let store = ServerDataStore.oneTimeEditor()
25-
26- store.airBase(area: areaId, base: rId)?.name = name
25+ store.async {
26+ store.airBase(area: areaId, base: rId)?.name = name
27+ }
2728 }
2829 }
--- a/KCD/AirCorpsSupplyCommand.swift
+++ b/KCD/AirCorpsSupplyCommand.swift
@@ -18,37 +18,39 @@ final class AirCorpsSupplyCommand: JSONCommand {
1818 override func execute() {
1919
2020 let store = ServerDataStore.oneTimeEditor()
21-
22- guard let areaId = parameter["api_area_id"].int else { return }
23- guard let rId = parameter["api_base_id"].int else { return }
24- guard let airBase = store.airBase(area: areaId, base: rId) else { return }
25-
26- let planeInfos = data["api_plane_info"]
27- let planes = airBase.planeInfo
28-
29- parameter["api_squadron_id"]
30- .integerArray
31- .enumerated()
32- .forEach {
33-
34- guard planes.count >= $0.element else { return }
35- guard planeInfos.count > $0.offset else { return }
36- guard let plane = planes[$0.element - 1] as? AirBasePlaneInfo else { return }
37-
38- let planeInfo = planeInfos[$0.offset]
39-
40- if let v = planeInfo["api_cond"].int { plane.cond = v }
41- if let v = planeInfo["api_slotid"].int { plane.slotid = v }
42- if let v = planeInfo["api_state"].int { plane.state = v }
43- if let v = planeInfo["api_count"].int { plane.count = v }
44- if let v = planeInfo["api_max_count"].int { plane.max_count = v }
21+ store.async {
22+
23+ guard let areaId = self.parameter["api_area_id"].int else { return }
24+ guard let rId = self.parameter["api_base_id"].int else { return }
25+ guard let airBase = store.airBase(area: areaId, base: rId) else { return }
26+
27+ let planeInfos = self.data["api_plane_info"]
28+ let planes = airBase.planeInfo
29+
30+ self.parameter["api_squadron_id"]
31+ .integerArray
32+ .enumerated()
33+ .forEach {
34+
35+ guard planes.count >= $0.element else { return }
36+ guard planeInfos.count > $0.offset else { return }
37+ guard let plane = planes[$0.element - 1] as? AirBasePlaneInfo else { return }
38+
39+ let planeInfo = planeInfos[$0.offset]
40+
41+ if let v = planeInfo["api_cond"].int { plane.cond = v }
42+ if let v = planeInfo["api_slotid"].int { plane.slotid = v }
43+ if let v = planeInfo["api_state"].int { plane.state = v }
44+ if let v = planeInfo["api_count"].int { plane.count = v }
45+ if let v = planeInfo["api_max_count"].int { plane.max_count = v }
46+ }
47+
48+ if let v = self.data["api_distance"].int { airBase.distance = v }
49+
50+ guard let material = store.material() else { return }
51+
52+ if let v = self.data["api_after_bauxite"].int { material.bauxite = v }
53+ if let v = self.data["api_after_fuel"].int { material.fuel = v }
4554 }
46-
47- if let v = data["api_distance"].int { airBase.distance = v }
48-
49- guard let material = store.material() else { return }
50-
51- if let v = data["api_after_bauxite"].int { material.bauxite = v }
52- if let v = data["api_after_fuel"].int { material.fuel = v }
5355 }
5456 }
--- a/KCD/AnchorageRepairManager.swift
+++ b/KCD/AnchorageRepairManager.swift
@@ -55,12 +55,13 @@ final class AnchorageRepairManager: NSObject {
5555
5656 let ship = fleetManager?.fleets[fleetNumber - 1][position]
5757
58- return ship?.master_ship.stype.id
58+ return ServerDataStore.default.sync { ship?.master_ship.stype.id }
5959 }
6060
6161 private func shipTypeId(shipId: Int) -> Int? {
6262
63- return ServerDataStore.default.ship(by: shipId)?.master_ship.stype.id
63+ let store = ServerDataStore.default
64+ return store.sync { store.ship(by: shipId)?.master_ship.stype.id }
6465 }
6566
6667 private func needsReset(info: HenseiDidChangeUserInfo) -> Bool {
--- a/KCD/ApplySuppliesCommand.swift
+++ b/KCD/ApplySuppliesCommand.swift
@@ -18,19 +18,22 @@ final class ApplySuppliesCommand: JSONCommand {
1818 .forEach { (_, json) in
1919
2020 guard let i = json["api_id"].int else { return }
21- guard let ship = store.ship(by: i) else { return }
21+ guard let ship = store.sync(execute: { store.ship(by: i) }) else { return }
2222 guard let bull = json["api_bull"].int else { return }
2323 guard let fuel = json["api_fuel"].int else { return }
2424 guard let slots = json["api_onslot"].arrayObject as? [Int] else { return }
2525 guard slots.count > 4 else { return }
2626
27- ship.bull = bull
28- ship.fuel = fuel
29- ship.onslot_0 = slots[0]
30- ship.onslot_1 = slots[1]
31- ship.onslot_2 = slots[2]
32- ship.onslot_3 = slots[3]
33- ship.onslot_4 = slots[4]
27+ store.sync {
28+
29+ ship.bull = bull
30+ ship.fuel = fuel
31+ ship.onslot_0 = slots[0]
32+ ship.onslot_1 = slots[1]
33+ ship.onslot_2 = slots[2]
34+ ship.onslot_3 = slots[3]
35+ ship.onslot_4 = slots[4]
36+ }
3437 }
3538 }
3639 }
--- a/KCD/BasicMapper.swift
+++ b/KCD/BasicMapper.swift
@@ -35,11 +35,14 @@ final class BasicMapper: JSONMapper {
3535
3636 func commit() {
3737
38- let store = ServerDataStore.oneTimeEditor()
38+ configuration.editorStore.async(execute: commintInContext)
39+ }
40+ private func commintInContext() {
3941
40- guard let basic = store.basic() ?? store.createBasic() else {
41-
42- return Logger.shared.log("Can not Get Basic")
42+ guard let store = configuration.editorStore as? ServerDataStore,
43+ let basic = store.basic() ?? store.createBasic() else {
44+
45+ return Logger.shared.log("Can not Get Basic")
4346 }
4447
4548 registerElement(data, to: basic)
--- a/KCD/BookmarkListViewController.swift
+++ b/KCD/BookmarkListViewController.swift
@@ -149,20 +149,25 @@ extension BookmarkListViewController: NSTableViewDelegate, NSTableViewDataSource
149149 guard let items = info.draggingPasteboard().pasteboardItems else { return false }
150150
151151 let store = BookmarkManager.shared.editorStore
152- items.enumerated().forEach {
152+ store.sync {
153+ items.enumerated().forEach {
154+
155+ guard let data = $0.element.data(forType: .bookmarkItem) else { return }
156+ guard let uri = NSKeyedUnarchiver.unarchiveObject(with: data) as? URL else { return }
157+ guard let oID = self.managedObjectContext.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: uri) else { return }
158+ guard let bookmark = store.object(of: Bookmark.entity, with: oID) else { return }
159+
160+ bookmark.order = targetOrder + $0.offset + 1
161+ }
153162
154- guard let data = $0.element.data(forType: .bookmarkItem) else { return }
155- guard let uri = NSKeyedUnarchiver.unarchiveObject(with: data) as? URL else { return }
156- guard let oID = managedObjectContext.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: uri) else { return }
157- guard let bookmark = store.object(of: Bookmark.entity, with: oID) else { return }
163+ store.save(errorHandler: store.presentOnMainThread)
158164
159- bookmark.order = targetOrder + $0.offset + 1
165+ self.bookmarkController.rearrangeObjects()
166+ self.reorderingBoolmarks()
167+ self.bookmarkController.rearrangeObjects()
160168 }
161169
162- store.save(errorHandler: store.presentOnMainThread)
163- bookmarkController.rearrangeObjects()
164- reorderingBoolmarks()
165- bookmarkController.rearrangeObjects()
170+ tableView.reloadData()
166171
167172 return true
168173 }
--- a/KCD/BookmarkManager.swift
+++ b/KCD/BookmarkManager.swift
@@ -51,27 +51,36 @@ final class BookmarkManager: NSObject, NSMenuDelegate {
5151 return items
5252 }
5353
54- func createNewBookmark() -> Bookmark? {
54+ func createNewBookmark(configurator: @escaping (Bookmark) -> Bool) -> Bookmark? {
5555
5656 guard let maxOrder = bookmarksController.value(forKeyPath: "arrangedObjects.@max.order") as? Int else {
5757
5858 return Logger.shared.log("BookmarkManager: Can no convert max order to Int", value: nil)
5959 }
6060
61- guard let new = editorStore.createBookmark() else {
61+ let editorStore: BookmarkDataStore = BookmarkDataStore.oneTimeEditor()
62+
63+ guard let new = editorStore.sync(execute: { editorStore.createBookmark() }) else {
6264
6365 return Logger.shared.log("BookmarkManager: Can not insert BookMarkItem", value: nil)
6466 }
6567
66- new.identifier = String(format: "B%@", arguments: [NSDate()])
67- new.order = maxOrder + 100
68-
69- DispatchQueue.main.asyncAfter(deadline: .now()) {
68+ return editorStore.sync {
69+
70+ new.identifier = String(format: "B%@", arguments: [NSDate()])
71+ new.order = maxOrder + 100
7072
71- self.editorStore.save(errorHandler: self.editorStore.presentOnMainThread)
73+ if !configurator(new) {
74+
75+ editorStore.delete(new)
76+
77+ return nil
78+ }
79+
80+ editorStore.save(errorHandler: editorStore.presentOnMainThread)
81+
82+ return new
7283 }
73-
74- return new
7584 }
7685
7786 func menuNeedsUpdate(_ menu: NSMenu) {
--- a/KCD/BroserWindowController.swift
+++ b/KCD/BroserWindowController.swift
@@ -194,7 +194,7 @@ extension BroserWindowController {
194194 @IBAction func clearQuestList(_ sender: AnyObject?) {
195195
196196 let store = ServerDataStore.oneTimeEditor()
197- store.quests().forEach(store.delete)
197+ store.sync { store.quests().forEach(store.delete) }
198198 }
199199
200200 // call from menu item
--- a/KCD/CalculateDamageCommand.swift
+++ b/KCD/CalculateDamageCommand.swift
@@ -111,54 +111,65 @@ extension CalculateDamageCommand {
111111
112112 let store = TemporaryDataStore.oneTimeEditor()
113113
114- store.damages().forEach(store.delete)
114+ store.sync { store.damages().forEach(store.delete) }
115115 }
116116
117117 func applyDamage() {
118118
119119 let store = TemporaryDataStore.oneTimeEditor()
120120
121- let totalDamages = store.sortedDamagesById()
121+ let totalDamages = store.sync { store.sortedDamagesById() }
122122
123- let aStore = ServerDataStore.oneTimeEditor()
123+ let aStore = ServerDataStore.default
124124
125125 Debug.excute(level: .debug) {
126126
127127 print("-------")
128128
129- totalDamages.forEach {
130-
131- guard let ship = aStore.ship(by: $0.shipID) else { return }
132-
133- if ship.nowhp != $0.hp {
129+ store.sync {
130+ totalDamages.forEach { damage in
131+
132+ let shipId = damage.shipID
133+ guard let ship = aStore.sync(execute: { aStore.ship(by: shipId) }) else { return }
134134
135- print("\(ship.name)(\(ship.id)),HP \(ship.nowhp) -> \($0.hp)")
135+ let damagedHp = damage.hp
136+ aStore.sync {
137+ if ship.nowhp != damagedHp {
138+
139+ print("\(ship.name)(\(ship.id)),HP \(ship.nowhp) -> \(damagedHp)")
140+ }
141+ }
136142 }
137143 }
138144
139-
140145 print("------- End Battle")
141146 }
142147
143148 // 第二艦隊単独出撃で正しくデータが反映されるように逆順にして計算
144- totalDamages.reversed().forEach {
145-
146- guard let ship = aStore.ship(by: $0.shipID) else { return }
147-
148- ship.nowhp = $0.hp
149-
150- if $0.useDamageControl { removeFirstDamageControl(of: ship) }
149+ store.sync {
150+ totalDamages.reversed().forEach { damage in
151+
152+ let shipId = damage.shipID
153+ guard let ship = aStore.sync(execute: { aStore.ship(by: shipId) }) else { return }
154+
155+ let damagedHp = damage.hp
156+ aStore.sync { ship.nowhp = damagedHp }
157+
158+ if damage.useDamageControl { self.removeFirstDamageControl(of: shipId) }
159+ }
151160 }
152-
153161 }
154162
155163 func updateBattleCell() {
156164
157165 let store = TemporaryDataStore.default
158166
159- guard let battle = store.battle() else { return Logger.shared.log("Battle is invalid.") }
167+ guard let battle = store.sync(execute: { store.battle() }) else {
168+
169+ return Logger.shared.log("Battle is invalid.")
170+ }
160171
161- battle.battleCell = (battle.no == 0 ? nil : battle.no as NSNumber)
172+ store.sync { battle.battleCell = (battle.no == 0 ? nil : battle.no as NSNumber) }
162173
163174 Debug.excute(level: .debug) {
164175
@@ -189,22 +200,45 @@ extension CalculateDamageCommand {
189200 }
190201 }
191202
192- func removeFirstDamageControl(of ship: Ship) {
203+ func removeFirstDamageControl(of shipId: Int) {
193204
194- let store = ServerDataStore.default
195-
196- let (item, damageControl) = ship
197- .equippedItem
198- .lazy
199- .flatMap { $0 as? SlotItem }
200- .map { ($0, store.masterSlotItemID(by: $0.id)) }
201- .map { ($0.0, DamageControlID(rawValue: $0.1)) }
202- .filter { $0.1 != nil }
203- .first ?? (nil, nil)
204-
205- if let validDamageControl = damageControl {
205+ let store = ServerDataStore.oneTimeEditor()
206+ store.sync {
207+
208+ guard let ship = store.ship(by: shipId) else { return }
209+
210+ let (item, damageControl) = ship
211+ .equippedItem
212+ .lazy
213+ .flatMap { $0 as? SlotItem }
214+ .map { ($0, store.masterSlotItemID(by: $0.id)) }
215+ .map { ($0.0, DamageControlID(rawValue: $0.1)) }
216+ .filter { $0.1 != nil }
217+ .first ?? (nil, nil)
206218
207- switch validDamageControl {
219+ if let validDamageControl = damageControl {
220+
221+ switch validDamageControl {
222+ case .damageControl: break
223+
224+ case .goddes:
225+ ship.fuel = ship.maxFuel
226+ ship.bull = ship.maxBull
227+ }
228+
229+ guard let equiped = ship.equippedItem.array as? [SlotItem] else { return }
230+
231+ ship.equippedItem = NSOrderedSet(array: equiped.filter { $0 != item })
232+
233+ return
234+ }
235+
236+ // check extra slot
237+ let exItemId = store.sync { store.masterSlotItemID(by: ship.slot_ex) }
238+
239+ guard let exType = DamageControlID(rawValue: exItemId) else { return }
240+
241+ switch exType {
208242 case .damageControl: break
209243
210244 case .goddes:
@@ -212,26 +246,7 @@ extension CalculateDamageCommand {
212246 ship.bull = ship.maxBull
213247 }
214248
215- guard let equiped = ship.equippedItem.array as? [SlotItem] else { return }
216-
217- ship.equippedItem = NSOrderedSet(array: equiped.filter { $0 != item })
218-
219- return
249+ ship.slot_ex = -1
220250 }
221-
222- // check extra slot
223- let exItemId = store.masterSlotItemID(by: ship.slot_ex)
224-
225- guard let exType = DamageControlID(rawValue: exItemId) else { return }
226-
227- switch exType {
228- case .damageControl: break
229-
230- case .goddes:
231- ship.fuel = ship.maxFuel
232- ship.bull = ship.maxBull
233- }
234-
235- ship.slot_ex = -1
236251 }
237252 }
--- a/KCD/ChangeHenseiCommand.swift
+++ b/KCD/ChangeHenseiCommand.swift
@@ -77,8 +77,8 @@ final class ChangeHenseiCommand: JSONCommand {
7777
7878 if shipId == -1 {
7979
80- guard let ship = removeShip(deckNumber: deckNumber, index: shipIndex) else { return }
81- notify(type: .remove, fleetNumber: deckNumber, position: shipIndex, shipID: ship.id)
80+ guard let shipId = removeShip(deckNumber: deckNumber, index: shipIndex) else { return }
81+ notify(type: .remove, fleetNumber: deckNumber, position: shipIndex, shipID: shipId)
8282 return
8383 }
8484
@@ -92,22 +92,22 @@ final class ChangeHenseiCommand: JSONCommand {
9292 guard case 0..<Deck.maxShipCount = shipIndex else { return }
9393
9494 let store = ServerDataStore.oneTimeEditor()
95- guard let deck = store.deck(by: deckNumber) else { return }
95+ guard let deck = store.sync(execute: { store.deck(by: deckNumber) }) else { return }
9696
9797 // すでに編成されているか? どこに?
9898 let (shipDeckNumber, shipDeckIndex) = position(of: shipId)
9999
100100 // 配置しようとする位置に今配置されている艦娘
101- let replaceShipId = deck[shipIndex]?.id
101+ let replaceShipId = store.sync { deck[shipIndex]?.id }
102102
103103 // 艦隊に配備
104- deck.setShip(id: shipId, for: shipIndex)
104+ store.sync { deck.setShip(id: shipId, for: shipIndex) }
105105
106106 // 入れ替え
107107 if shipDeckNumber != nil {
108108
109- let shipDeck = store.deck(by: shipDeckNumber!)
110- shipDeck?.setShip(id: replaceShipId ?? -1, for: shipDeckIndex)
109+ let shipDeck = store.sync { store.deck(by: shipDeckNumber!) }
110+ store.sync { shipDeck?.setShip(id: replaceShipId ?? -1, for: shipDeckIndex) }
111111 shipDeck.map { packFleet(store: store, deck: $0) }
112112 }
113113
@@ -132,40 +132,47 @@ final class ChangeHenseiCommand: JSONCommand {
132132
133133 private func position(of shipId: Int) -> (deckNumber: Int?, shipId: Int) {
134134
135- return ServerDataStore.default
136- .decksSortedById()
137- .lazy
138- .enumerated()
139- .map { (idx, deck) -> (Int, [Ship]) in (idx + 1, deck[0..<Deck.maxShipCount]) }
140- .filter { $0.1.contains { $0.id == shipId } }
141- .map { (deck, ships) in (deck, ships.index(where: { $0.id == shipId })!) }
142- .first ?? (nil, -1)
135+ let store = ServerDataStore.default
136+ return store.sync {
137+ store
138+ .decksSortedById()
139+ .lazy
140+ .enumerated()
141+ .map { (idx, deck) -> (Int, [Ship]) in (idx + 1, deck[0..<Deck.maxShipCount]) }
142+ .filter { $0.1.contains { $0.id == shipId } }
143+ .map { (deck, ships) in (deck, ships.index(where: { $0.id == shipId })!) }
144+ .first ?? (nil, -1)
145+ }
143146 }
144147
145- private func removeShip(deckNumber: Int, index: Int) -> Ship? {
148+ private func removeShip(deckNumber: Int, index: Int) -> Int? {
146149
147150 let store = ServerDataStore.oneTimeEditor()
148151
149- guard let deck = store.deck(by: deckNumber) else { return Logger.shared.log("Deck not found", value: nil) }
152+ guard let deck = store.sync(execute: { store.deck(by: deckNumber) }) else {
153+
154+ return Logger.shared.log("Deck not found", value: nil)
155+ }
150156
151- let shipId = deck[index]?.id ?? -1
152- deck.setShip(id: -1, for: index)
157+ let shipId = store.sync { deck[index]?.id ?? -1 }
158+ store.sync { deck.setShip(id: -1, for: index) }
153159
154160 packFleet(store: store, deck: deck)
155161
156- return ServerDataStore.default.ship(by: shipId)
162+ return shipId
157163 }
158164
159165 private func excludeShipsWithoutFlagShip(deckNumber: Int) {
160166
161167 let store = ServerDataStore.oneTimeEditor()
162-
163- guard let deck = store.deck(by: deckNumber) else {
168+ store.sync {
169+ guard let deck = store.deck(by: deckNumber) else {
170+
171+ return Logger.shared.log("Deck not found")
172+ }
164173
165- return Logger.shared.log("Deck not found")
174+ (1..<Deck.maxShipCount).forEach { deck.setShip(id: -1, for: $0) }
166175 }
167-
168- (1..<Deck.maxShipCount).forEach { deck.setShip(id: -1, for: $0) }
169176 }
170177
171178 private func packFleet(store: ServerDataStore, deck: Deck) {
@@ -181,7 +188,7 @@ final class ChangeHenseiCommand: JSONCommand {
181188 set(newShips, at: index + 1, in: deck)
182189 }
183190
184- set(deck[0..<Deck.maxShipCount], at: 0, in: deck)
191+ store.sync { set(deck[0..<Deck.maxShipCount], at: 0, in: deck) }
185192 }
186193
187194 private func notify(type: ChangeHenseiType,
--- a/KCD/CoreDataManager.swift
+++ b/KCD/CoreDataManager.swift
@@ -36,6 +36,10 @@ protocol CoreDataProvider {
3636
3737 protocol CoreDataAccessor: CoreDataProvider {
3838
39+ func sync(execute: () -> Void)
40+ func sync<T>(execute: () -> T) -> T
41+ func async(execute: @escaping () -> Void)
42+
3943 func insertNewObject<T>(for entity: Entity<T>) -> T?
4044 func delete(_ object: NSManagedObject)
4145 func object<T>(of entity: Entity<T>, with objectId: NSManagedObjectID) -> T?
@@ -79,40 +83,50 @@ extension CoreDataProvider {
7983
8084 func save() throws {
8185
82- guard context.commitEditing() else {
83-
84- throw CoreDataError.couldNotSave("\(String(describing: type(of: self))) unable to commit editing before saveing")
85- }
86-
87- do {
86+ var caughtError: Error?
87+ context.performAndWait {
8888
89- try context.save()
90-
91- } catch let error as NSError {
92-
93- throw CoreDataError.couldNotSave(error.localizedDescription)
94- }
95-
96- guard let parent = context.parent else { return }
97-
98- // save parent context
99- var catchedError: NSError? = nil
100- parent.performAndWait {
89+ guard context.commitEditing() else {
90+
91+ caughtError = CoreDataError.couldNotSave("\(String(describing: type(of: self))) unable to commit editing before saveing")
92+ return
93+ }
10194
10295 do {
10396
104- try parent.save()
97+ try context.save()
10598
10699 } catch let error as NSError {
107100
108- catchedError = error
101+ caughtError = CoreDataError.couldNotSave(error.localizedDescription)
102+ return
103+ }
104+
105+ guard let parent = context.parent else { return }
106+
107+ // save parent context
108+ var catchedError: NSError? = nil
109+ parent.performAndWait {
110+
111+ do {
112+
113+ try parent.save()
114+
115+ } catch let error as NSError {
116+
117+ catchedError = error
118+ }
119+ }
120+
121+ if let error = catchedError {
122+
123+ caughtError = CoreDataError.couldNotSave(error.localizedDescription)
109124 }
110125 }
111126
112- if let error = catchedError {
113-
114- throw CoreDataError.couldNotSave(error.localizedDescription)
127+ if let error = caughtError {
115128
129+ throw error
116130 }
117131 }
118132
@@ -134,6 +148,25 @@ extension CoreDataProvider {
134148
135149 extension CoreDataAccessor {
136150
151+ func sync(execute work: () -> Void) {
152+
153+ self.context.performAndWait(work)
154+ }
155+
156+ func sync<T>(execute work: () -> T) -> T {
157+
158+ var value: T!
159+ sync {
160+ value = work()
161+ }
162+ return value
163+ }
164+
165+ func async(execute work: @escaping () -> Void) {
166+
167+ self.context.perform(work)
168+ }
169+
137170 func insertNewObject<T>(for entity: Entity<T>) -> T? {
138171
139172 return NSEntityDescription.insertNewObject(forEntityName: entity.name, into: context) as? T
@@ -155,11 +188,31 @@ extension CoreDataAccessor {
155188 req.sortDescriptors = sortDescriptors
156189 req.predicate = predicate
157190
158- return try context.fetch(req)
191+ var result: [T]?
192+ var caughtError: Error?
193+ sync {
194+ do {
195+
196+ result = try self.context.fetch(req)
197+ } catch {
198+
199+ caughtError = error
200+ }
201+ }
202+ if let error = caughtError {
203+
204+ throw error
205+ }
206+
207+ return result ?? []
159208 }
160209
161210 func object(with objectId: NSManagedObjectID) -> NSManagedObject {
162211
163- return context.object(with: objectId)
212+ var result: NSManagedObject?
213+ sync {
214+ result = self.context.object(with: objectId)
215+ }
216+ return result!
164217 }
165218 }
--- a/KCD/CreateShipCommand.swift
+++ b/KCD/CreateShipCommand.swift
@@ -31,29 +31,53 @@ final class CreateShipCommand: JSONCommand {
3131
3232 let store = ServerDataStore.default
3333
34- guard let kenzoDock = store.kenzoDock(by: dockId),
35- let flagShip = store.deck(by: 1)?[0],
36- let basic = store.basic() else {
37-
38- return Logger.shared.log("CreateShipCommand: CoreData is wrong")
34+ let storedInfos: KenzoMarkCommand.KenzoDockInfo? = store.sync {
35+
36+ guard let kenzoDock = store.kenzoDock(by: dockId) else {
37+
38+ return nil
39+ }
40+
41+ return KenzoMarkCommand.KenzoDockInfo(dockId: kenzoDock.id,
42+ shipId: kenzoDock.created_ship_id,
43+ fuel: kenzoDock.item1,
44+ bull: kenzoDock.item2,
45+ steel: kenzoDock.item3,
46+ bauxite: kenzoDock.item4,
47+ kaihatusizai: kenzoDock.item5)
3948 }
4049
41- let localStore = LocalDataStore.oneTimeEditor()
50+ guard let infos = storedInfos else {
51+
52+ return Logger.shared.log("Can not load KenzoDeck")
53+ }
4254
43- guard let newMark = localStore.kenzoMark(byDockId: dockId) ?? localStore.createKenzoMark() else {
55+ guard let flagShip = store.sync(execute: { store.deck(by: 1)?[0] }) else {
4456
45- return Logger.shared.log("Can not create KenzoMark")
57+ return Logger.shared.log("Can not load deck")
58+ }
59+ guard let commanderLv = store.sync(execute: { store.basic()?.level }) else {
60+
61+ return Logger.shared.log("Can not load basic")
4662 }
4763
48- newMark.fuel = kenzoDock.item1
49- newMark.bull = kenzoDock.item2
50- newMark.steel = kenzoDock.item3
51- newMark.bauxite = kenzoDock.item4
52- newMark.kaihatusizai = kenzoDock.item5
53- newMark.created_ship_id = kenzoDock.created_ship_id
54- newMark.flagShipName = flagShip.name
55- newMark.flagShipLv = flagShip.lv
56- newMark.commanderLv = basic.level
57- newMark.kDockId = dockId
64+ let localStore = LocalDataStore.oneTimeEditor()
65+ localStore.sync {
66+ guard let newMark = localStore.kenzoMark(byDockId: dockId) ?? localStore.createKenzoMark() else {
67+
68+ return Logger.shared.log("Can not create KenzoMark")
69+ }
70+
71+ newMark.fuel = infos.fuel
72+ newMark.bull = infos.bull
73+ newMark.steel = infos.steel
74+ newMark.bauxite = infos.bauxite
75+ newMark.kaihatusizai = infos.kaihatusizai
76+ newMark.created_ship_id = infos.shipId
77+ newMark.flagShipName = store.sync { flagShip.name }
78+ newMark.flagShipLv = store.sync { flagShip.lv }
79+ newMark.commanderLv = commanderLv
80+ newMark.kDockId = dockId
81+ }
5882 }
5983 }
--- a/KCD/DamageCalculator.swift
+++ b/KCD/DamageCalculator.swift
@@ -34,48 +34,60 @@ extension DamageCalculator {
3434
3535 func calculateBattle() {
3636
37- calcKouku()
38- calcOpeningTaisen()
39- calcOpeningAttack()
40- calcHougeki1()
41- calcHougeki2()
42- calcHougeki3()
43- calcRaigeki()
37+ store.sync {
38+
39+ self.calcKouku()
40+ self.calcOpeningTaisen()
41+ self.calcOpeningAttack()
42+ self.calcHougeki1()
43+ self.calcHougeki2()
44+ self.calcHougeki3()
45+ self.calcRaigeki()
46+ }
4447 }
4548
4649 func calcCombinedBattleAir() {
4750
48- calcKouku()
49- calcOpeningTaisen()
50- calcOpeningAttack()
51- calcHougeki1()
52- calcRaigeki()
53- calcHougeki2()
54- calcHougeki3()
51+ store.sync {
52+
53+ self.calcKouku()
54+ self.calcOpeningTaisen()
55+ self.calcOpeningAttack()
56+ self.calcHougeki1()
57+ self.calcRaigeki()
58+ self.calcHougeki2()
59+ self.calcHougeki3()
60+ }
5561 }
5662
5763 func calcEachBattleAir() {
5864
59- calcKouku()
60- calcOpeningTaisen()
61- calcOpeningAttack()
62- calcHougeki1()
63- calcHougeki2()
64- calcRaigeki()
65- calcHougeki3()
65+ store.sync {
66+
67+ self.calcKouku()
68+ self.calcOpeningTaisen()
69+ self.calcOpeningAttack()
70+ self.calcHougeki1()
71+ self.calcHougeki2()
72+ self.calcRaigeki()
73+ self.calcHougeki3()
74+ }
6675 }
6776
6877 func calcEachNightToDay() {
6978
70- calcNightHogeki1()
71- calcNightHogeki2()
72- calcKouku()
73- calcOpeningTaisen()
74- calcOpeningAttack()
75- calcHougeki1()
76- calcHougeki2()
77- calcRaigeki()
78- calcHougeki3()
79+ store.sync {
80+
81+ self.calcNightHogeki1()
82+ self.calcNightHogeki2()
83+ self.calcKouku()
84+ self.calcOpeningTaisen()
85+ self.calcOpeningAttack()
86+ self.calcHougeki1()
87+ self.calcHougeki2()
88+ self.calcRaigeki()
89+ self.calcHougeki3()
90+ }
7991 }
8092
8193 func calcEnemyCombinedBattle() {
@@ -86,7 +98,10 @@ extension DamageCalculator {
8698
8799 func calcMidnight() {
88100
89- calculateMidnightBattle()
101+ store.sync {
102+
103+ self.calculateMidnightBattle()
104+ }
90105 }
91106 }
92107
@@ -212,12 +227,14 @@ extension DamageCalculator {
212227
213228 func setShip(_ ship: Ship, into damage: Damage) {
214229
215- damage.shipID = ship.id
216- damage.hp = ship.nowhp
230+ let sStore = ServerDataStore.default
231+
232+ damage.shipID = sStore.sync { ship.id }
233+ damage.hp = sStore.sync { ship.nowhp }
217234
218235 Debug.excute(level: .debug) {
219-
220- print("add Damage entity of \(ship.name) at \(damage.id)")
236+ let name = sStore.sync { ship.name }
237+ print("add Damage entity of \(name) at \(damage.id)")
221238 }
222239 }
223240
@@ -235,13 +252,15 @@ extension DamageCalculator {
235252
236253 guard let battle = store.battle() else { return Logger.shared.log("Battle is invalid.") }
237254
255+ let sStore = ServerDataStore.default
238256 // 第一艦隊
239- let firstFleetShips = ServerDataStore.default.ships(byDeckId: battle.deckId)
257+ let deckId = battle.deckId
258+ let firstFleetShips = sStore.sync { sStore.ships(byDeckId: deckId) }
240259
241260 // 第二艦隊
242261 if isCombinedBattle {
243262
244- let secondFleetShips = ServerDataStore.default.ships(byDeckId: 2)
263+ let secondFleetShips = sStore.sync { sStore.ships(byDeckId: 2) }
245264 buildDamages(first: firstFleetShips, second: secondFleetShips)
246265 } else {
247266
@@ -298,9 +317,10 @@ extension DamageCalculator {
298317
299318 Debug.excute(level: .debug) {
300319
301- if receive != 0, let ship = ServerDataStore.default.ship(by: damage.shipID) {
320+ let store = ServerDataStore.default
321+ if receive != 0, let shipName = store.sync(execute: { store.ship(by: damage.shipID)?.name }) {
302322
303- print("\(ship.name) recieve Damage \(receive)")
323+ print("\(shipName) recieve Damage \(receive)")
304324 }
305325 }
306326
@@ -308,7 +328,8 @@ extension DamageCalculator {
308328
309329 if damage.hp > 0 { return }
310330
311- guard let ship = ServerDataStore.default.ship(by: damage.shipID) else { return }
331+ let sStore = ServerDataStore.default
332+ guard let ship = sStore.sync(execute: { sStore.ship(by: damage.shipID) }) else { return }
312333
313334 damage.hp = damageControlIfPossible(ship: ship)
314335 damage.useDamageControl = (damage.hp != 0)
@@ -408,18 +429,34 @@ extension DamageCalculator {
408429 private func damageControlIfPossible(ship: Ship) -> Int {
409430
410431 let store = ServerDataStore.default
411-
412- let damageControl = ship
413- .equippedItem
414- .lazy
415- .flatMap { $0 as? SlotItem }
416- .map { store.masterSlotItemID(by: $0.id) }
417- .flatMap { DamageControlID(rawValue: $0) }
418- .first
419-
420- if let validDamageControl = damageControl {
432+ return store.sync {
433+ let damageControl = ship
434+ .equippedItem
435+ .lazy
436+ .flatMap { $0 as? SlotItem }
437+ .map { store.masterSlotItemID(by: $0.id) }
438+ .flatMap { DamageControlID(rawValue: $0) }
439+ .first
440+
441+ if let validDamageControl = damageControl {
442+
443+ switch validDamageControl {
444+ case .damageControl:
445+ Debug.print("Damage Control", level: .debug)
446+ return Int(Double(ship.maxhp) * 0.2)
447+
448+ case .goddes:
449+ Debug.print("Goddes", level: .debug)
450+ return ship.maxhp
451+ }
452+ }
453+
454+ // check extra slot
455+ let exItemId = store.masterSlotItemID(by: ship.slot_ex)
421456
422- switch validDamageControl {
457+ guard let exType = DamageControlID(rawValue: exItemId) else { return 0 }
458+
459+ switch exType {
423460 case .damageControl:
424461 Debug.print("Damage Control", level: .debug)
425462 return Int(Double(ship.maxhp) * 0.2)
@@ -429,20 +466,5 @@ extension DamageCalculator {
429466 return ship.maxhp
430467 }
431468 }
432-
433- // check extra slot
434- let exItemId = store.masterSlotItemID(by: ship.slot_ex)
435-
436- guard let exType = DamageControlID(rawValue: exItemId) else { return 0 }
437-
438- switch exType {
439- case .damageControl:
440- Debug.print("Damage Control", level: .debug)
441- return Int(Double(ship.maxhp) * 0.2)
442-
443- case .goddes:
444- Debug.print("Goddes", level: .debug)
445- return ship.maxhp
446- }
447469 }
448470 }
--- a/KCD/DestroyItem2Command.swift
+++ b/KCD/DestroyItem2Command.swift
@@ -19,9 +19,9 @@ final class DestroyItem2Command: JSONCommand {
1919
2020 let store = ServerDataStore.oneTimeEditor()
2121
22- store.slotItems(in: parameter["api_slotitem_ids"].integerArray).forEach(store.delete)
22+ store.sync { store.slotItems(in: self.parameter["api_slotitem_ids"].integerArray).forEach(store.delete) }
2323
24- guard let material = store.material() else {
24+ guard let material = store.sync(execute: { store.material() }) else {
2525
2626 return Logger.shared.log("Material is not found")
2727 }
@@ -31,9 +31,12 @@ final class DestroyItem2Command: JSONCommand {
3131 return Logger.shared.log("api_get_material is wrong")
3232 }
3333
34- material.fuel += getMaterials[0]
35- material.bull += getMaterials[1]
36- material.steel += getMaterials[2]
37- material.bauxite += getMaterials[3]
34+ store.sync {
35+
36+ material.fuel += getMaterials[0]
37+ material.bull += getMaterials[1]
38+ material.steel += getMaterials[2]
39+ material.bauxite += getMaterials[3]
40+ }
3841 }
3942 }
--- a/KCD/DropShipHistoryCommand.swift
+++ b/KCD/DropShipHistoryCommand.swift
@@ -22,48 +22,54 @@ final class DropShipHistoryCommand: JSONCommand {
2222 guard let shipName = data["api_get_ship"]["api_ship_name"].string else { return }
2323 guard let winRank = data["api_win_rank"].string else { return }
2424
25- guard let battle = TemporaryDataStore.default.battle() else {
25+ let tempStore = TemporaryDataStore.default
26+ guard let battle = tempStore.sync(execute: { tempStore.battle() }) else {
2627
2728 return Logger.shared.log("Can not get Battle")
2829 }
2930
30- let mapAreaId = battle.mapArea
31+ let mapAreaId = tempStore.sync { battle.mapArea }
32+ let mapInfoId = tempStore.sync { battle.mapInfo }
3133
3234 let store = ServerDataStore.default
3335
34- guard let mapInfo = store.mapInfo(area: mapAreaId, no: battle.mapInfo) else {
36+ guard let mapInfoName = store.sync(execute: { store.mapInfo(area: mapAreaId, no: mapInfoId)?.name }) else {
3537
3638 return Logger.shared.log("KCMasterMapInfo is not found")
3739 }
3840
39- guard let mapArea = store.mapArea(by: mapAreaId) else {
41+ guard let mapAreaName = store.sync(execute: { store.mapArea(by: mapAreaId)?.name }) else {
4042
4143 return Logger.shared.log("KCMasterMapArea is not found")
4244 }
4345
4446
4547 let localStore = LocalDataStore.oneTimeEditor()
46- guard let new = localStore.createHiddenDropShipHistory() else {
48+ localStore.sync {
4749
48- return Logger.shared.log("Can not create HiddenDropShipHistory")
50+ guard let new = localStore.createHiddenDropShipHistory() else {
51+
52+ return Logger.shared.log("Can not create HiddenDropShipHistory")
53+ }
54+
55+ new.shipName = shipName
56+ new.mapArea = "\(mapAreaId)"
57+ new.mapAreaName = mapAreaName
58+ new.mapInfo = tempStore.sync { battle.mapInfo }
59+ new.mapInfoName = mapInfoName
60+ new.mapCell = tempStore.sync { battle.no }
61+ new.winRank = winRank
62+ new.date = Date()
4963 }
50-
51- new.shipName = shipName
52- new.mapArea = "\(mapAreaId)"
53- new.mapAreaName = mapArea.name
54- new.mapInfo = battle.mapInfo
55- new.mapInfoName = mapInfo.name
56- new.mapCell = battle.no
57- new.winRank = winRank
58- new.date = Date()
5964 }
6065
6166 private func storeToVisible() {
6267
6368 let store = LocalDataStore.oneTimeEditor()
64-
65- let hidden = store.hiddenDropShipHistories()
66- _ = hidden.map(store.createDropShipHistory(from:))
67- hidden.forEach(store.delete)
69+ store.sync {
70+ let hidden = store.hiddenDropShipHistories()
71+ _ = hidden.map(store.createDropShipHistory(from:))
72+ hidden.forEach(store.delete)
73+ }
6874 }
6975 }
--- a/KCD/DummyShipCommand.swift
+++ b/KCD/DummyShipCommand.swift
@@ -39,16 +39,14 @@ final class DummyShipCommand: JSONCommand {
3939 guard DummyShipCommand.needsEnterDummy else { return }
4040
4141 let store = ServerDataStore.oneTimeEditor()
42-
43- store.createShip()?.id = -2
42+ store.sync { store.createShip()?.id = -2 }
4443 DummyShipCommand.needsEnterDummy = false
4544 }
4645
4746 private func removeDummy() {
4847
4948 let store = ServerDataStore.oneTimeEditor()
50-
51- store.ships(by: -2).forEach(store.delete)
49+ store.sync { store.ships(by: -2).forEach(store.delete) }
5250 DummyShipCommand.needsEnterDummy = false
5351 }
5452 }
--- a/KCD/ExternalBrowserWindowController.swift
+++ b/KCD/ExternalBrowserWindowController.swift
@@ -235,15 +235,18 @@ extension ExternalBrowserWindowController {
235235 @IBAction func addBookmark(_ sender: AnyObject?) {
236236
237237 guard let window = window else { return }
238- guard let bookmark = BookmarkManager.shared.createNewBookmark() else { return }
239-
240- bookmark.name = window.title
241- bookmark.urlString = webView.mainFrameURL
242- bookmark.windowContentSize = windowContentSize
243- bookmark.contentVisibleRect = contentVisibleRect
244- bookmark.canResize = canResize
245- bookmark.canScroll = canScroll
246- bookmark.scrollDelay = 0.5
238+ _ = BookmarkManager.shared.createNewBookmark { bookmark in
239+
240+ bookmark.name = window.title
241+ bookmark.urlString = self.webView.mainFrameURL
242+ bookmark.windowContentSize = self.windowContentSize
243+ bookmark.contentVisibleRect = self.contentVisibleRect
244+ bookmark.canResize = self.canResize
245+ bookmark.canScroll = self.canScroll
246+ bookmark.scrollDelay = 0.5
247+
248+ return true
249+ }
247250 }
248251
249252 @IBAction func showBookmark(_ sender: AnyObject?) {
--- a/KCD/Fleet.swift
+++ b/KCD/Fleet.swift
@@ -46,6 +46,8 @@ final class Fleet: NSObject {
4646 @objc dynamic private(set) var ships: [Ship] = []
4747 private var deck: Deck?
4848
49+ let store = ServerDataStore.default
50+
4951 init?(number: Int) {
5052
5153 guard case 1...4 = number else {
@@ -58,7 +60,7 @@ final class Fleet: NSObject {
5860
5961 super.init()
6062
61- if let deck = ServerDataStore.default.deck(by: number) {
63+ if let deck = store.sync(execute: { self.store.deck(by: number) }) {
6264
6365 self.setupDeck(deck: deck)
6466
@@ -68,7 +70,7 @@ final class Fleet: NSObject {
6870 ServerDataStore.default
6971 .future { _ -> Deck? in
7072
71- guard let deck = ServerDataStore.default.deck(by: number) else { return .none }
73+ guard let deck = self.store.sync(execute: { self.store.deck(by: number) }) else { return .none }
7274
7375 return deck
7476 }
@@ -82,7 +84,7 @@ final class Fleet: NSObject {
8284 }
8385 }
8486
85- subscript(_ index: Int) -> Ship? { return deck?[index] }
87+ subscript(_ index: Int) -> Ship? { return store.sync { self.deck?[index] } }
8688
8789 private func setupDeck(deck: Deck) {
8890
@@ -95,6 +97,6 @@ final class Fleet: NSObject {
9597
9698 private func setupShips(deck: Deck) {
9799
98- ships = deck[0...6]
100+ ships = store.sync { deck[0...6] }
99101 }
100102 }
--- a/KCD/FleetManager.swift
+++ b/KCD/FleetManager.swift
@@ -64,16 +64,18 @@ final class FleetManager: NSObject {
6464 private func setNewFleetNumberToShip() {
6565
6666 let store = ServerDataStore.oneTimeEditor()
67-
68- // clear all
69- store.shipsInFleet().forEach { $0.fleet = 0 as NSNumber }
70-
71- // set
72- fleets.enumerated().forEach { (index, fleet) in
67+ store.sync {
7368
74- fleet.ships.forEach {
69+ // clear all
70+ store.shipsInFleet().forEach { $0.fleet = 0 as NSNumber }
71+
72+ // set
73+ self.fleets.enumerated().forEach { index, fleet in
7574
76- store.ship(by: $0.id)?.fleet = (index + 1) as NSNumber
75+ fleet.ships.forEach { ship in
76+
77+ store.ship(by: ship.id)?.fleet = (index + 1) as NSNumber
78+ }
7779 }
7880 }
7981 }
--- a/KCD/GuardShelterCommand.swift
+++ b/KCD/GuardShelterCommand.swift
@@ -42,19 +42,20 @@ final class GuardShelterCommand: JSONCommand {
4242
4343 private func damagedShipId(damagedPos: Int) -> Int? {
4444
45- let firstDeckId = TemporaryDataStore.default.battle()?.deckId ?? 1
45+ let tempStore = TemporaryDataStore.default
46+ let firstDeckId = tempStore.sync { tempStore.battle()?.deckId ?? 1 }
4647
4748 let store = ServerDataStore.default
4849
4950 switch firstDeckId {
5051 case 1:
5152 switch damagedPos {
52- case 1...6: return store.deck(by: 1)?.shipId(of: damagedPos - 1)
53- case 7...12: return store.deck(by: 2)?.shipId(of: damagedPos - 6 - 1)
53+ case 1...6: return store.sync { store.deck(by: 1)?.shipId(of: damagedPos - 1) }
54+ case 7...12: return store.sync { store.deck(by: 2)?.shipId(of: damagedPos - 6 - 1) }
5455 default: return nil
5556 }
5657 case 3:
57- return store.deck(by: 3)?.shipId(of: damagedPos - 1)
58+ return store.sync { store.deck(by: 3)?.shipId(of: damagedPos - 1) }
5859 default:
5960 return nil
6061 }
@@ -71,40 +72,44 @@ final class GuardShelterCommand: JSONCommand {
7172 }
7273
7374 let store = TemporaryDataStore.oneTimeEditor()
74-
75- guard let damaged = store.createGuardEscaped() else {
75+ store.sync {
7676
77- return Logger.shared.log("Can not create GuardEscaped for damaged")
78- }
79-
80- damaged.shipID = damagedId
81- damaged.ensured = false
82-
83- // store guardian if needs
84- guard let guardianPos = escape["api_tow_idx"][0].int else { return }
85-
86- let fixedGuardianPos = guardianPos - 6 - 1
87-
88- guard let guardianId = ServerDataStore.default.deck(by: 2)?.shipId(of: fixedGuardianPos) else {
77+ guard let damaged = store.createGuardEscaped() else {
78+
79+ return Logger.shared.log("Can not create GuardEscaped for damaged")
80+ }
8981
90- return Logger.shared.log("guardianPos is wrong")
91- }
92-
93- guard let guardian = store.createGuardEscaped() else {
82+ damaged.shipID = damagedId
83+ damaged.ensured = false
84+
85+ // store guardian if needs
86+ guard let guardianPos = escape["api_tow_idx"][0].int else { return }
87+
88+ let fixedGuardianPos = guardianPos - 6 - 1
9489
95- return Logger.shared.log("Can not create GuardEscaped for guardinan")
90+ let sStore = ServerDataStore.default
91+ guard let guardianId = sStore.sync(execute: { sStore.deck(by: 2)?.shipId(of: fixedGuardianPos) }) else {
92+
93+ return Logger.shared.log("guardianPos is wrong")
94+ }
95+
96+ guard let guardian = store.createGuardEscaped() else {
97+
98+ return Logger.shared.log("Can not create GuardEscaped for guardinan")
99+ }
100+
101+ guardian.shipID = guardianId
102+ guardian.ensured = false
96103 }
97-
98- guardian.shipID = guardianId
99- guardian.ensured = false
100104 }
101105
102106 private func removeInvalidEntry() {
103107
104108 let store = TemporaryDataStore.oneTimeEditor()
105-
106- store.notEnsuredGuardEscaped().forEach(store.delete)
107- store.save(errorHandler: store.presentOnMainThread)
109+ store.sync {
110+ store.notEnsuredGuardEscaped().forEach(store.delete)
111+ store.save(errorHandler: store.presentOnMainThread)
112+ }
108113 Thread.sleep(forTimeInterval: 0.1)
109114 notify()
110115 }
@@ -112,9 +117,10 @@ final class GuardShelterCommand: JSONCommand {
112117 private func removeAllEntry() {
113118
114119 let store = TemporaryDataStore.oneTimeEditor()
115-
116- store.guardEscaped().forEach(store.delete)
117- store.save(errorHandler: store.presentOnMainThread)
120+ store.sync {
121+ store.guardEscaped().forEach(store.delete)
122+ store.save(errorHandler: store.presentOnMainThread)
123+ }
118124 Thread.sleep(forTimeInterval: 0.1)
119125 notify()
120126 }
@@ -122,9 +128,10 @@ final class GuardShelterCommand: JSONCommand {
122128 private func ensureGuardShelter() {
123129
124130 let store = TemporaryDataStore.oneTimeEditor()
125-
126- store.guardEscaped().forEach { $0.ensured = true }
127- store.save(errorHandler: store.presentOnMainThread)
131+ store.sync {
132+ store.guardEscaped().forEach { $0.ensured = true }
133+ store.save(errorHandler: store.presentOnMainThread)
134+ }
128135 Thread.sleep(forTimeInterval: 0.1)
129136 notify()
130137 }
--- a/KCD/HistoryItemCleaner.swift
+++ b/KCD/HistoryItemCleaner.swift
@@ -15,10 +15,13 @@ final class HistoryItemCleaner {
1515 guard UserDefaults.standard[.cleanOldHistoryItems] else { return }
1616
1717 let store = LocalDataStore.oneTimeEditor()
18- let cleanSinceDays = UserDefaults.standard[.cleanSinceDays]
19-
20- store.unmarkedKaihatuHistories(befor: cleanSinceDays).forEach(store.delete)
21- store.unmarkedKenzoHistories(befor: cleanSinceDays).forEach(store.delete)
22- store.unmarkedDropShipHistories(befor: cleanSinceDays).forEach(store.delete)
18+ store.sync {
19+
20+ let cleanSinceDays = UserDefaults.standard[.cleanSinceDays]
21+
22+ store.unmarkedKaihatuHistories(befor: cleanSinceDays).forEach(store.delete)
23+ store.unmarkedKenzoHistories(befor: cleanSinceDays).forEach(store.delete)
24+ store.unmarkedDropShipHistories(befor: cleanSinceDays).forEach(store.delete)
25+ }
2326 }
2427 }
--- a/KCD/HistoryTableViewController.swift
+++ b/KCD/HistoryTableViewController.swift
@@ -49,10 +49,12 @@ class HistoryTableViewController: NSViewController {
4949 guard let selection = controller.selectedObjects as? [NSManagedObject] else { return }
5050
5151 let selectedIndex = controller.selectionIndex
52- selection
53- .map { $0.objectID }
54- .map(store.object(with:))
55- .forEach(store.delete)
52+ store.sync {
53+ selection
54+ .map { $0.objectID }
55+ .map(store.object(with:))
56+ .forEach(store.delete)
57+ }
5658
5759 if selectedIndex > 1 {
5860
@@ -70,14 +72,16 @@ class HistoryTableViewController: NSViewController {
7072 let predicate = NSPredicate(#keyPath(KenzoMark.date), equal: clickedObject.date)
7173
7274 let store = LocalDataStore.oneTimeEditor()
73-
74- if let items = try? objects(of: predicate, in: store),
75- var history = items.first as? Markable {
75+ store.sync {
76+
77+ if let items = try? self.objects(of: predicate, in: store),
78+ var history = items.first as? Markable {
79+
80+ history.mark = !history.mark
81+ }
7682
77- history.mark = !history.mark
83+ store.save(errorHandler: store.presentOnMainThread)
7884 }
79-
80- store.save(errorHandler: store.presentOnMainThread)
8185 }
8286
8387 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
--- a/KCD/JSONMapper.swift
+++ b/KCD/JSONMapper.swift
@@ -144,7 +144,7 @@ extension JSONMapper {
144144 }
145145 }
146146
147- func commit() {
147+ private func commintInContext() {
148148
149149 let store = configuration.editorStore
150150
@@ -174,6 +174,14 @@ extension JSONMapper {
174174 store.save(errorHandler: store.presentOnMainThread)
175175 }
176176
177+ func commit() {
178+
179+ configuration.editorStore
180+ .sync {
181+ self.commintInContext()
182+ }
183+ }
184+
177185 func beginRegister(_ object: ObjectType) {}
178186
179187 func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool {
--- a/KCD/KaisouLockCommand.swift
+++ b/KCD/KaisouLockCommand.swift
@@ -28,6 +28,6 @@ final class KaisouLockCommand: JSONCommand {
2828
2929 let store = ServerDataStore.oneTimeEditor()
3030
31- store.slotItem(by: slotId)?.locked = (locked != 0)
31+ store.sync { store.slotItem(by: slotId)?.locked = (locked != 0) }
3232 }
3333 }
--- a/KCD/KenzoMarkCommand.swift
+++ b/KCD/KenzoMarkCommand.swift
@@ -10,48 +10,69 @@ import Cocoa
1010
1111 final class KenzoMarkCommand: JSONCommand {
1212
13+ struct KenzoDockInfo {
14+ let dockId: Int
15+ let shipId: Int
16+ let fuel: Int
17+ let bull: Int
18+ let steel: Int
19+ let bauxite: Int
20+ let kaihatusizai: Int
21+ }
22+
1323 override func execute() {
1424
25+ let store = LocalDataStore.oneTimeEditor()
26+ store.sync {
27+ self.executeInContext(localStore: store)
28+ }
29+ }
30+
31+ private func executeInContext(localStore: LocalDataStore) {
32+
1533 guard let kdockId = parameter["api_kdock_id"].int else {
1634
1735 return Logger.shared.log("api_kdock_id is wrong")
1836 }
1937
2038 let store = ServerDataStore.default
21-
22- guard let kenzoDock = store.kenzoDock(by: kdockId) else {
39+ guard let kenzoDock = store.sync(execute: { store.kenzoDock(by: kdockId) }) else {
2340
24- return Logger.shared.log("KenzoDock is not fount")
41+ return Logger.shared.log("KenzoDock is not found")
2542 }
26-
27- guard let flagShip = store.masterShip(by: kenzoDock.created_ship_id) else {
43+ let kenzoDockInfo = store.sync {
44+ KenzoDockInfo(dockId: kenzoDock.id,
45+ shipId: kenzoDock.created_ship_id,
46+ fuel: kenzoDock.item1,
47+ bull: kenzoDock.item2,
48+ steel: kenzoDock.item3,
49+ bauxite: kenzoDock.item4,
50+ kaihatusizai: kenzoDock.item5)
51+ }
52+ guard let flagShip = store.sync(execute: { store.masterShip(by: kenzoDock.created_ship_id) }) else {
2853
2954 return Logger.shared.log("MasterShip is not found")
3055 }
3156
32- let localStore = LocalDataStore.oneTimeEditor()
3357 guard let new = localStore.createKenzoHistory() else {
3458
3559 return Logger.shared.log("Can not create KenzoHistory")
3660 }
3761
38- new.name = flagShip.name
39- new.sTypeId = flagShip.stype.id
40- new.fuel = kenzoDock.item1
41- new.bull = kenzoDock.item2
42- new.steel = kenzoDock.item3
43- new.bauxite = kenzoDock.item4
44- new.kaihatusizai = kenzoDock.item5
62+ new.name = store.sync { flagShip.name }
63+ new.sTypeId = store.sync { flagShip.stype.id }
64+ new.fuel = kenzoDockInfo.fuel
65+ new.bull = kenzoDockInfo.bull
66+ new.steel = kenzoDockInfo.steel
67+ new.bauxite = kenzoDockInfo.bauxite
68+ new.kaihatusizai = kenzoDockInfo.kaihatusizai
4569 new.date = Date()
46- (new.flagShipLv, new.flagShipName, new.commanderLv) = markedValues(kenzoDock: kenzoDock, kdockId: kdockId)
70+ (new.flagShipLv, new.flagShipName, new.commanderLv) = markedValues(docInfo: kenzoDockInfo, in: localStore)
4771 }
4872
49- private func markedValues(kenzoDock: KenzoDock, kdockId: Int) -> (Int, String, Int) {
50-
51- let store = LocalDataStore.default
73+ private func markedValues(docInfo: KenzoDockInfo, in store: LocalDataStore) -> (Int, String, Int) {
5274
53- if let kenzoMark = store.kenzoMark(kenzoDock: kenzoDock,
54- kDockId: kdockId) {
75+ if let kenzoMark = store.kenzoMark(docInfo: docInfo) {
5576
5677 return (kenzoMark.flagShipLv, kenzoMark.flagShipName, kenzoMark.commanderLv)
5778 }
--- a/KCD/LocalDataStoreAccessor.swift
+++ b/KCD/LocalDataStoreAccessor.swift
@@ -80,16 +80,16 @@ extension LocalDataStore {
8080 return kenzoMarks.first
8181 }
8282
83- func kenzoMark(kenzoDock: KenzoDock, kDockId: Int) -> KenzoMark? {
83+ func kenzoMark(docInfo: KenzoMarkCommand.KenzoDockInfo) -> KenzoMark? {
8484
8585 let predicate = NSPredicate.empty
86- .and(NSPredicate(#keyPath(KenzoMark.fuel), equal: kenzoDock.item1))
87- .and(NSPredicate(#keyPath(KenzoMark.bull), equal: kenzoDock.item2))
88- .and(NSPredicate(#keyPath(KenzoMark.steel), equal: kenzoDock.item3))
89- .and(NSPredicate(#keyPath(KenzoMark.bauxite), equal: kenzoDock.item4))
90- .and(NSPredicate(#keyPath(KenzoMark.kaihatusizai), equal: kenzoDock.item5))
91- .and(NSPredicate(#keyPath(KenzoMark.created_ship_id), equal: kenzoDock.created_ship_id))
92- .and(NSPredicate(#keyPath(KenzoMark.kDockId), equal: kDockId))
86+ .and(NSPredicate(#keyPath(KenzoMark.kDockId), equal: docInfo.dockId))
87+ .and(NSPredicate(#keyPath(KenzoMark.fuel), equal: docInfo.fuel))
88+ .and(NSPredicate(#keyPath(KenzoMark.bull), equal: docInfo.bull))
89+ .and(NSPredicate(#keyPath(KenzoMark.steel), equal: docInfo.steel))
90+ .and(NSPredicate(#keyPath(KenzoMark.bauxite), equal: docInfo.bauxite))
91+ .and(NSPredicate(#keyPath(KenzoMark.kaihatusizai), equal: docInfo.kaihatusizai))
92+ .and(NSPredicate(#keyPath(KenzoMark.created_ship_id), equal: docInfo.shipId))
9393
9494 guard let kenzoMarks = try? objects(of: KenzoMark.entity, predicate: predicate) else { return nil }
9595
--- a/KCD/MapInfoCommand.swift
+++ b/KCD/MapInfoCommand.swift
@@ -18,9 +18,10 @@ final class MapInfoCommand: JSONCommand {
1818 override func execute() {
1919
2020 let store = ServerDataStore.oneTimeEditor()
21-
22- store.airBases().forEach(store.delete)
23- store.save(errorHandler: store.presentOnMainThread)
21+ store.sync {
22+ store.airBases().forEach(store.delete)
23+ store.save(errorHandler: store.presentOnMainThread)
24+ }
2425
2526 AirBaseMapper(apiResponse).commit()
2627 }
--- a/KCD/MapStartCommand.swift
+++ b/KCD/MapStartCommand.swift
@@ -34,7 +34,7 @@ final class MapStartCommand: JSONCommand {
3434
3535 private func startBattle() {
3636
37- store.battles().forEach(store.delete)
37+ store.sync { self.store.battles().forEach(self.store.delete) }
3838
3939 guard let deckId = parameter["api_deck_id"].int,
4040 let mapArea = parameter["api_maparea_id"].int,
@@ -48,18 +48,21 @@ final class MapStartCommand: JSONCommand {
4848 return Logger.shared.log("startBattle JSON is wrong")
4949 }
5050
51- guard let battle = store.createBattle() else {
51+ guard let battle = store.sync(execute: { self.store.createBattle() }) else {
5252
5353 return Logger.shared.log("Can not create Battle")
5454 }
5555
5656 let kcd = ServerDataStore.default
5757
58- battle.deckId = deckId
59- battle.mapArea = mapArea
60- battle.mapInfo = mapInfo
61- battle.no = no
62- battle.firstFleetShipsCount = kcd.ships(byDeckId: deckId).count
58+ store.sync {
59+
60+ battle.deckId = deckId
61+ battle.mapArea = mapArea
62+ battle.mapInfo = mapInfo
63+ battle.no = no
64+ battle.firstFleetShipsCount = kcd.sync { kcd.ships(byDeckId: deckId).count }
65+ }
6366 }
6467
6568 private func nextCell() {
@@ -70,14 +73,23 @@ final class MapStartCommand: JSONCommand {
7073 return Logger.shared.log("updateBattleCell JSON is wrong")
7174 }
7275
73- guard let battle = store.battle() else { return Logger.shared.log("Battle is invalid.") }
76+ guard let battle = store.sync(execute: { self.store.battle() }) else {
77+
78+ return Logger.shared.log("Battle is invalid.")
79+ }
7480
75- battle.no = cellNumber
76- battle.isBossCell = (eventId == 5)
81+ store.sync {
82+
83+ battle.no = cellNumber
84+ battle.isBossCell = (eventId == 5)
85+ }
7786 }
7887
7988 private func updateBattleCell() {
8089
81- store.battle().map { $0.battleCell = nil }
90+ store.sync {
91+
92+ self.store.battle().map { $0.battleCell = nil }
93+ }
8294 }
8395 }
--- a/KCD/MasterShipMapper.swift
+++ b/KCD/MasterShipMapper.swift
@@ -23,7 +23,9 @@ final class MasterShipMapper: JSONMapper {
2323
2424 private lazy var masterSTypes: [MasterSType] = {
2525
26- return ServerDataStore.default.sortedMasterSTypesById()
26+ guard let store = configuration.editorStore as? ServerDataStore else { return [] }
27+
28+ return store.sortedMasterSTypesById()
2729 }()
2830
2931 func handleExtraValue(_ value: JSON, forKey key: String, to masterShip: MasterShip) -> Bool {
--- a/KCD/MaterialMapper.swift
+++ b/KCD/MaterialMapper.swift
@@ -43,6 +43,10 @@ final class MaterialMapper: JSONMapper {
4343
4444 func commit() {
4545
46+ configuration.editorStore.sync(execute: commintInContext)
47+ }
48+ private func commintInContext() {
49+
4650 guard let store = configuration.editorStore as? ServerDataStore,
4751 let material = store.material() ?? store.createMaterial() else {
4852
--- a/KCD/NyukyoSpeedChangeCommand.swift
+++ b/KCD/NyukyoSpeedChangeCommand.swift
@@ -18,13 +18,14 @@ final class NyukyoSpeedChangeCommand: JSONCommand {
1818 override func execute() {
1919
2020 let store = ServerDataStore.oneTimeEditor()
21-
22- let nDock = parameter["api_ndock_id"].int.flatMap(store.nyukyoDock(by:))
23-
24- nDock.flatMap { $0.ship_id }.flatMap(store.ship(by:)).map { $0.nowhp = $0.maxhp }
25- nDock?.ship_id = 0
26- nDock?.state = 0
27-
28- store.material().map { $0.kousokushuhuku = $0.kousokushuhuku - 1 }
21+ store.sync {
22+ let nDock = self.parameter["api_ndock_id"].int.flatMap(store.nyukyoDock(by:))
23+
24+ nDock.flatMap { $0.ship_id }.flatMap(store.ship(by:)).map { $0.nowhp = $0.maxhp }
25+ nDock?.ship_id = 0
26+ nDock?.state = 0
27+
28+ store.material().map { $0.kousokushuhuku = $0.kousokushuhuku - 1 }
29+ }
2930 }
3031 }
--- a/KCD/NyukyoStartCommand.swift
+++ b/KCD/NyukyoStartCommand.swift
@@ -20,13 +20,14 @@ final class NyukyoStartCommand: JSONCommand {
2020 guard let hi = parameter["api_highspeed"].int, hi != 0 else { return }
2121
2222 let store = ServerDataStore.oneTimeEditor()
23-
24- parameter["api_ship_id"]
25- .int
26- .flatMap(store.ship(by:))
27- .map { $0.nowhp = $0.maxhp }
28-
29- store.material()
30- .map { $0.kousokushuhuku = $0.kousokushuhuku - 1 }
23+ store.sync {
24+ self.parameter["api_ship_id"]
25+ .int
26+ .flatMap(store.ship(by:))
27+ .map { $0.nowhp = $0.maxhp }
28+
29+ store.material()
30+ .map { $0.kousokushuhuku = $0.kousokushuhuku - 1 }
31+ }
3132 }
3233 }
--- a/KCD/QuestListCommand.swift
+++ b/KCD/QuestListCommand.swift
@@ -36,31 +36,36 @@ final class QuestListCommand: JSONCommand {
3636 let lastQuestNumber = questNumberList?.last ?? 9999
3737 let minNo = (page == 1) ? 0 : firstQuestNumber
3838 let maxNo = (page == pageCount) ? 9999 : lastQuestNumber
39- store.quests(in: minNo...maxNo).forEach { $0.state = 1 }
39+ store.sync { store.quests(in: minNo...maxNo).forEach { $0.state = 1 } }
4040
4141 // 新しいデータ投入
42- let quests = store.sortedQuestByNo()
42+ let quests = store.sync { store.sortedQuestByNo() }
4343 questList.forEach { _, quest in
4444
4545 guard let no = quest["api_no"].int else { return }
4646
47- let t = quests.binarySearch { $0.no ==? no }
47+ let t = store.sync { quests.binarySearch { $0.no ==? no } }
4848
49- guard let new = t ?? store.createQuest() else { return Logger.shared.log("Can not create Quest") }
49+ guard let new = t ?? store.sync(execute: { store.createQuest() }) else {
50+
51+ return Logger.shared.log("Can not create Quest")
52+ }
5053
51- new.bonus_flag = quest["api_bonus_flag"].int.map { $0 != 0 } ?? false
52- new.category = quest["api_category"].int ?? 0
53- new.detail = quest["api_detail"].string ?? ""
54-// new.get_material_0 = questData["api_get_material_0"] as? Int ?? 0
55-// new.get_material_1 = questData["api_get_material_1"] as? Int ?? 0
56-// new.get_material_2 = questData["api_get_material_2"] as? Int ?? 0
57-// new.get_material_3 = questData["api_get_material_3"] as? Int ?? 0
58- new.invalid_flag = quest["api_invalid_flag"].int ?? 0
59- new.no = quest["api_no"].int ?? 0
60- new.progress_flag = quest["api_progress_flag"].int ?? 0
61- new.state = quest["api_state"].int ?? 0
62- new.title = quest["api_title"].string ?? ""
63- new.type = quest["api_type"].int ?? 0
54+ store.sync {
55+ new.bonus_flag = quest["api_bonus_flag"].int.map { $0 != 0 } ?? false
56+ new.category = quest["api_category"].int ?? 0
57+ new.detail = quest["api_detail"].string ?? ""
58+// new.get_material_0 = questData["api_get_material_0"] as? Int ?? 0
59+// new.get_material_1 = questData["api_get_material_1"] as? Int ?? 0
60+// new.get_material_2 = questData["api_get_material_2"] as? Int ?? 0
61+// new.get_material_3 = questData["api_get_material_3"] as? Int ?? 0
62+ new.invalid_flag = quest["api_invalid_flag"].int ?? 0
63+ new.no = quest["api_no"].int ?? 0
64+ new.progress_flag = quest["api_progress_flag"].int ?? 0
65+ new.state = quest["api_state"].int ?? 0
66+ new.title = quest["api_title"].string ?? ""
67+ new.type = quest["api_type"].int ?? 0
68+ }
6469 }
6570 }
6671 }
--- a/KCD/RealDestroyShipCommand.swift
+++ b/KCD/RealDestroyShipCommand.swift
@@ -13,21 +13,23 @@ final class RealDestroyShipCommand: JSONCommand {
1313 override func execute() {
1414
1515 let store = ServerDataStore.oneTimeEditor()
16-
17- let ships = parameter["api_ship_id"]
18- .integerArray
19- .flatMap(store.ship(by:))
20-
21- if parameter["api_slot_dest_flag"].int == 0 {
16+ store.sync {
17+
18+ let ships = self.parameter["api_ship_id"]
19+ .integerArray
20+ .flatMap(store.ship(by:))
2221
23- // remove allEquipment
24- ships.forEach {
22+ if self.parameter["api_slot_dest_flag"].int == 0 {
23+
24+ // remove allEquipment
25+ ships.forEach {
2526 $0.equippedItem = []
2627 $0.extraItem = nil
28+ }
2729 }
30+
31+ // destory ships
32+ ships.forEach(store.delete)
2833 }
29-
30- // destory ships
31- ships.forEach(store.delete)
3234 }
3335 }
--- a/KCD/RealPowerUpCommand.swift
+++ b/KCD/RealPowerUpCommand.swift
@@ -13,10 +13,11 @@ final class RealPowerUpCommand: JSONCommand {
1313 override func execute() {
1414
1515 let store = ServerDataStore.oneTimeEditor()
16-
17- parameter["api_id_items"]
18- .integerArray
19- .flatMap(store.ship(by:))
20- .forEach(store.delete)
16+ store.sync {
17+ self.parameter["api_id_items"]
18+ .integerArray
19+ .flatMap(store.ship(by:))
20+ .forEach(store.delete)
21+ }
2122 }
2223 }
--- a/KCD/RemodelSlotItemCommand.swift
+++ b/KCD/RemodelSlotItemCommand.swift
@@ -20,32 +20,33 @@ final class RemodelSlotItemCommand: JSONCommand {
2020
2121 let afterSlot = data["api_after_slot"]
2222 let store = ServerDataStore.oneTimeEditor()
23-
24- guard let slotItem = store.slotItem(by: slotItemId) else {
23+ store.sync {
24+ guard let slotItem = store.slotItem(by: slotItemId) else {
25+
26+ return Logger.shared.log("SlotItem not found")
27+ }
2528
26- return Logger.shared.log("SlotItem not found")
27- }
28-
29- if let locked = afterSlot["api_locked"].int {
30-
31- slotItem.locked = (locked != 0)
32- }
33- if let masterSlotItemId = afterSlot["api_slotitem_id"].int,
34- masterSlotItemId != slotItem.slotitem_id,
35- let masterSlotItem = store.masterSlotItem(by: slotItemId) {
29+ if let locked = afterSlot["api_locked"].int {
30+
31+ slotItem.locked = (locked != 0)
32+ }
33+ if let masterSlotItemId = afterSlot["api_slotitem_id"].int,
34+ masterSlotItemId != slotItem.slotitem_id,
35+ let masterSlotItem = store.masterSlotItem(by: slotItemId) {
36+
37+ slotItem.master_slotItem = masterSlotItem
38+ slotItem.slotitem_id = slotItemId
39+
40+ }
41+ if let level = afterSlot["api_level"].int {
42+
43+ slotItem.level = level
44+ }
3645
37- slotItem.master_slotItem = masterSlotItem
38- slotItem.slotitem_id = slotItemId
46+ // remove used slot items.
47+ guard let useSlot = self.data["api_use_slot_id"].arrayObject as? [Int] else { return }
3948
49+ store.slotItems(in: useSlot).forEach(store.delete)
4050 }
41- if let level = afterSlot["api_level"].int {
42-
43- slotItem.level = level
44- }
45-
46- // remove used slot items.
47- guard let useSlot = data["api_use_slot_id"].arrayObject as? [Int] else { return }
48-
49- store.slotItems(in: useSlot).forEach(store.delete)
5051 }
5152 }
--- a/KCD/ResetSortie.swift
+++ b/KCD/ResetSortie.swift
@@ -14,6 +14,6 @@ final class ResetSortie {
1414
1515 let store = TemporaryDataStore.oneTimeEditor()
1616
17- store.battles().forEach(store.delete)
17+ store.sync { store.battles().forEach(store.delete) }
1818 }
1919 }
--- a/KCD/ResourceHistoryManager.swift
+++ b/KCD/ResourceHistoryManager.swift
@@ -63,40 +63,41 @@ final class ResourceHistoryManager: NSObject {
6363 return Logger.shared.log("ResourceHistoryManager: Can not get Material")
6464 }
6565
66- guard let basic = store.basic() else {
66+ guard let experience = store.sync(execute: { store.basic()?.experience }) else {
6767
6868 return Logger.shared.log("ResourceHistoryManager: Can not get Basic")
6969 }
7070
7171 let historyStore = ResourceHistoryDataStore.oneTimeEditor()
72-
73- guard let newHistory = historyStore.createResource() else {
72+ historyStore.sync {
73+ guard let newHistory = historyStore.createResource() else {
74+
75+ return Logger.shared.log("ResourceHistoryManager: Can not create ResourceHIstory")
76+ }
77+
78+ let now = Date()
79+ var nowComp = Calendar.current
80+ .dateComponents([.year, .month, .day, .hour, .minute], from: now)
81+ let minutes = nowComp.minute.map { ($0 + 2) / 5 } ?? 0
82+ nowComp.minute = minutes * 5
7483
75- return Logger.shared.log("ResourceHistoryManager: Can not create ResourceHIstory")
84+ newHistory.date = Calendar.current.date(from: nowComp)!
85+ newHistory.minute = (minutes != 60) ? minutes : 0
86+ newHistory.fuel = store.sync { material.fuel }
87+ newHistory.bull = store.sync { material.bull }
88+ newHistory.steel = store.sync { material.steel }
89+ newHistory.bauxite = store.sync { material.bauxite }
90+ newHistory.kaihatusizai = store.sync { material.kaihatusizai }
91+ newHistory.kousokukenzo = store.sync { material.kousokukenzo }
92+ newHistory.kousokushuhuku = store.sync { material.kousokushuhuku }
93+ newHistory.screw = store.sync { material.screw }
94+ newHistory.experience = experience
7695 }
77-
78- let now = Date()
79- var nowComp = Calendar.current
80- .dateComponents([.year, .month, .day, .hour, .minute], from: now)
81- let minutes = nowComp.minute.map { ($0 + 2) / 5 } ?? 0
82- nowComp.minute = minutes * 5
83-
84- newHistory.date = Calendar.current.date(from: nowComp)!
85- newHistory.minute = (minutes != 60) ? minutes : 0
86- newHistory.fuel = material.fuel
87- newHistory.bull = material.bull
88- newHistory.steel = material.steel
89- newHistory.bauxite = material.bauxite
90- newHistory.kaihatusizai = material.kaihatusizai
91- newHistory.kousokukenzo = material.kousokukenzo
92- newHistory.kousokushuhuku = material.kousokushuhuku
93- newHistory.screw = material.screw
94- newHistory.experience = basic.experience
9596 }
9697
9798 private func reduceResourceByConditions(_ store: ResourceHistoryDataStore, _ target: [Int], _ ago: Date) {
9899
99- store.resources(in: target, older: ago).forEach(store.delete)
100+ store.sync { store.resources(in: target, older: ago).forEach(store.delete) }
100101 }
101102
102103 private func dateOfMonth(_ month: Int) -> Date {
--- a/KCD/SetActionCommand.swift
+++ b/KCD/SetActionCommand.swift
@@ -27,10 +27,11 @@ final class SetActionCommand: JSONCommand {
2727 if rIds.count != actions.count { print("missmatch count") }
2828
2929 let store = ServerDataStore.oneTimeEditor()
30-
31- zip(rIds, actions).forEach { (rId, action) in
32-
33- store.airBase(area: areaId, base: rId)?.action_kind = action
30+ store.sync {
31+ zip(rIds, actions).forEach { rId, action in
32+
33+ store.airBase(area: areaId, base: rId)?.action_kind = action
34+ }
3435 }
3536 }
3637 }
--- a/KCD/SetPlaneCommand.swift
+++ b/KCD/SetPlaneCommand.swift
@@ -37,29 +37,31 @@ final class SetPlaneCommand: JSONCommand {
3737 }
3838
3939 let store = ServerDataStore.oneTimeEditor()
40-
41- guard let airbase = store.airBase(area: areaId, base: rId) else {
40+ store.sync {
4241
43- return Logger.shared.log("AirBase is not found")
44- }
45-
46- let planes = airbase.planeInfo
47-
48- guard planes.count >= squadronId,
49- let plane = planes[squadronId - 1] as? AirBasePlaneInfo else {
42+ guard let airbase = store.airBase(area: areaId, base: rId) else {
5043
51- return Logger.shared.log("AirBase is wrong")
44+ return Logger.shared.log("AirBase is not found")
45+ }
46+
47+ let planes = airbase.planeInfo
48+
49+ guard planes.count >= squadronId,
50+ let plane = planes[squadronId - 1] as? AirBasePlaneInfo else {
51+
52+ return Logger.shared.log("AirBase is wrong")
53+ }
54+
55+ // TODO: state が 2 の時のみ cond, count, max_count がnilであることを許すようにする
56+ plane.cond = planInfo["api_cond"].int ?? 0
57+ plane.slotid = slotid
58+ plane.state = state
59+ plane.count = planInfo["api_count"].int ?? 0
60+ plane.max_count = planInfo["api_max_count"].int ?? 0
61+
62+ airbase.distance = distance
63+
64+ self.data["api_after_bauxite"].int.map { store.material()?.bauxite = $0 }
5265 }
53-
54- // TODO: state が 2 の時のみ cond, count, max_count がnilであることを許すようにする
55- plane.cond = planInfo["api_cond"].int ?? 0
56- plane.slotid = slotid
57- plane.state = state
58- plane.count = planInfo["api_count"].int ?? 0
59- plane.max_count = planInfo["api_max_count"].int ?? 0
60-
61- airbase.distance = distance
62-
63- data["api_after_bauxite"].int.map { store.material()?.bauxite = $0 }
6466 }
6567 }
--- a/KCD/ShipMapper.swift
+++ b/KCD/ShipMapper.swift
@@ -61,12 +61,18 @@ final class ShipMapper: JSONMapper {
6161 private var registerIds: [Int] = []
6262 private lazy var masterShips: [MasterShip] = {
6363
64- return ServerDataStore.default.sortedMasterShipsById()
64+ guard let store = configuration.editorStore as? ServerDataStore else { return [] }
65+
66+ return store.sortedMasterShipsById()
67+
6568 }()
6669 private lazy var slotItems: [SlotItem] = {
6770
68- return ServerDataStore.default.sortedSlotItemsById()
71+ guard let store = configuration.editorStore as? ServerDataStore else { return [] }
72+
73+ return store.sortedSlotItemsById()
6974 }()
75+
7076 private var needsDeleteUnregisteredShip: Bool {
7177
7278 switch apiResponse.api.endpoint {
--- a/KCD/ShipMasterDetailWindowController.swift
+++ b/KCD/ShipMasterDetailWindowController.swift
@@ -68,30 +68,31 @@ final class ShipMasterDetailWindowController: NSWindowController {
6868 @IBAction func applySally(_ sender: AnyObject?) {
6969
7070 let store = ServerDataStore.oneTimeEditor()
71-
72- guard let i = selectedShip?.objectID else { return }
73- guard let ship = store.object(of: Ship.entity, with: i) else { return }
74-//
75-// ship.sally_area = sally.integerValue as NSNumber
76-
77- let eq = ship.equippedItem.array
78- let slotId = sally.integerValue
79-
80- let pos = min(4, eq.count)
81-
82- if eq.count > 4 {
71+ store.sync {
72+ guard let i = self.selectedShip?.objectID else { return }
73+ guard let ship = store.object(of: Ship.entity, with: i) else { return }
74+ //
75+ // ship.sally_area = sally.integerValue as NSNumber
8376
84- ship.equippedItem = NSOrderedSet(array: Array(eq.dropLast()))
85- }
86-
87- if let slotItem = store.slotItem(by: slotId) {
77+ let eq = ship.equippedItem.array
78+ let slotId = self.sally.integerValue
79+
80+ let pos = min(4, eq.count)
8881
89- ship.setItem(slotId, to: pos)
82+ if eq.count > 4 {
83+
84+ ship.equippedItem = NSOrderedSet(array: Array(eq.dropLast()))
85+ }
9086
91- ship.equippedItem = NSOrderedSet(array: ship.equippedItem.array + [slotItem])
87+ if let slotItem = store.slotItem(by: slotId) {
88+
89+ ship.setItem(slotId, to: pos)
90+
91+ ship.equippedItem = NSOrderedSet(array: ship.equippedItem.array + [slotItem])
92+ }
93+
94+ store.save(errorHandler: {_ in})
9295 }
93-
94- store.save(errorHandler: {_ in})
9596 }
9697 }
9798
--- a/KCD/SlotItemMapper.swift
+++ b/KCD/SlotItemMapper.swift
@@ -38,7 +38,10 @@ final class SlotItemMapper: JSONMapper {
3838
3939 private var registerIds: [Int] = []
4040 private lazy var masterSlotItems: [MasterSlotItem] = {
41- return ServerDataStore.default.sortedMasterSlotItemsById()
41+
42+ guard let store = configuration.editorStore as? ServerDataStore else { return [] }
43+
44+ return store.sortedMasterSlotItemsById()
4245 }()
4346
4447 func beginRegister(_ slotItem: SlotItem) {
--- a/KCD/SlotResetCommand.swift
+++ b/KCD/SlotResetCommand.swift
@@ -18,31 +18,33 @@ final class SlotResetCommand: JSONCommand {
1818 override func execute() {
1919
2020 let store = ServerDataStore.oneTimeEditor()
21-
22- guard let ship = parameter["api_id"].int.flatMap(store.ship(by:)) else {
23-
24- return Logger.shared.log("api_id is wrong")
25- }
26- guard let slotItems = data["api_slot"].arrayObject as? [Int] else {
21+ store.sync {
2722
28- return Logger.shared.log("Can not parse api_data.api_slot")
29- }
30-
31- zip(slotItems, 0...).forEach(ship.setItem)
32-
33- let storedSlotItems = store.sortedSlotItemsById()
34- let newSet = slotItems
35- .flatMap { slotItem -> SlotItem? in
23+ guard let ship = self.parameter["api_id"].int.flatMap(store.ship(by:)) else {
3624
37- guard slotItem > 0 else { return nil }
25+ return Logger.shared.log("api_id is wrong")
26+ }
27+ guard let slotItems = self.data["api_slot"].arrayObject as? [Int] else {
3828
39- let found = storedSlotItems.binarySearch { $0.id ==? slotItem }
40- if let item = found { return item }
41- print("Item \(slotItem) is not found")
42-
43- return nil
29+ return Logger.shared.log("Can not parse api_data.api_slot")
30+ }
31+
32+ zip(slotItems, 0...).forEach(ship.setItem)
33+
34+ let storedSlotItems = store.sortedSlotItemsById()
35+ let newSet = slotItems
36+ .flatMap { slotItem -> SlotItem? in
37+
38+ guard slotItem > 0 else { return nil }
39+
40+ let found = storedSlotItems.binarySearch { $0.id ==? slotItem }
41+ if let item = found { return item }
42+ print("Item \(slotItem) is not found")
43+
44+ return nil
45+ }
46+
47+ ship.equippedItem = NSOrderedSet(array: newSet)
4448 }
45-
46- ship.equippedItem = NSOrderedSet(array: newSet)
4749 }
4850 }
--- a/KCD/StoreCreateSlotItemHistoryCommand.swift
+++ b/KCD/StoreCreateSlotItemHistoryCommand.swift
@@ -26,31 +26,35 @@ final class StoreCreateSlotItemHistoryCommand: JSONCommand {
2626 let numberOfUsedKaihatuSizai = (success != 0 ? 1 : 0)
2727
2828 let store = ServerDataStore.default
29-
30- guard let flagShip = store.deck(by: 1)?[0] else {
29+ guard let flagShip = store.sync(execute: { store.deck(by: 1)?[0] }) else {
3130
3231 return Logger.shared.log("Flagship is not found")
3332 }
3433
35- guard let basic = store.basic() else { return print("Basic is wrong") }
34+ guard let commanderLv = store.sync(execute: { store.basic()?.level }) else {
35+
36+ return print("Basic is wrong")
37+ }
3638
3739 let localStore = LocalDataStore.oneTimeEditor()
38-
39- guard let newHistory = localStore.createKaihatuHistory() else {
40+ guard let newHistory = localStore.sync(execute: { localStore.createKaihatuHistory() }) else {
4041
4142 return Logger.shared.log("Can not create new KaihatuHistory entry")
4243 }
4344
44- newHistory.name = name
45- newHistory.fuel = fuel
46- newHistory.bull = bull
47- newHistory.steel = steel
48- newHistory.bauxite = bauxite
49- newHistory.kaihatusizai = numberOfUsedKaihatuSizai
50- newHistory.flagShipLv = flagShip.lv
51- newHistory.flagShipName = flagShip.name
52- newHistory.commanderLv = basic.level
53- newHistory.date = Date()
45+ localStore.sync {
46+
47+ newHistory.name = name
48+ newHistory.fuel = fuel
49+ newHistory.bull = bull
50+ newHistory.steel = steel
51+ newHistory.bauxite = bauxite
52+ newHistory.kaihatusizai = numberOfUsedKaihatuSizai
53+ newHistory.flagShipLv = store.sync { flagShip.lv }
54+ newHistory.flagShipName = store.sync { flagShip.name }
55+ newHistory.commanderLv = commanderLv
56+ newHistory.date = Date()
57+ }
5458 }
5559
5660 private func masterSlotItemName(sccess: Int, data: JSON) -> String {
@@ -65,6 +69,7 @@ final class StoreCreateSlotItemHistoryCommand: JSONCommand {
6569 return Logger.shared.log("api_slotitem_id is wrong", value: "")
6670 }
6771
68- return ServerDataStore.default.masterSlotItem(by: slotItemId)?.name ?? ""
72+ let store = ServerDataStore.default
73+ return store.sync { store.masterSlotItem(by: slotItemId)?.name ?? "" }
6974 }
7075 }
--- a/KCD/TSVSupport.swift
+++ b/KCD/TSVSupport.swift
@@ -95,10 +95,17 @@ final class TSVSupport {
9595
9696 guard $0 == .OK else { return }
9797 guard let url = panel.url else { return }
98- guard let kaihatuHistory = self.dataOfKaihatuHistory() else { return }
99- guard let kenzoHistory = self.dataOfKenzoHistory() else { return }
100- guard let kenzoMark = self.dataOfKenzoMark() else { return }
101- guard let dropShipHistory = self.dataOfDropShipHistory() else { return }
98+
99+ let data = self.store.sync { () -> (Data, Data, Data, Data)? in
100+
101+ guard let kaihatuHistory = self.dataOfKaihatuHistory() else { return nil }
102+ guard let kenzoHistory = self.dataOfKenzoHistory() else { return nil }
103+ guard let kenzoMark = self.dataOfKenzoMark() else { return nil }
104+ guard let dropShipHistory = self.dataOfDropShipHistory() else { return nil }
105+
106+ return (kaihatuHistory, kenzoHistory, kenzoMark, dropShipHistory)
107+ }
108+ guard let (kaihatuHistory, kenzoHistory, kenzoMark, dropShipHistory) = data else { return }
102109
103110 let fileW = FileWrapper(directoryWithFileWrappers: [:])
104111 fileW.addRegularFile(withContents: kaihatuHistory, preferredFilename: "kaihatu.tsv")
@@ -217,7 +224,6 @@ final class TSVSupport {
217224 private func registerKaihatuHistory(_ data: Data) {
218225
219226 let array = String(data: data, encoding: .utf8)?.components(separatedBy: "\n")
220- let store = LocalDataStore.oneTimeEditor()
221227 array?.forEach {
222228
223229 let attr = $0.components(separatedBy: "\t")
@@ -254,7 +260,6 @@ final class TSVSupport {
254260 private func registerKenzoHistory(_ data: Data) {
255261
256262 let array = String(data: data, encoding: .utf8)?.components(separatedBy: "\n")
257- let store = LocalDataStore.oneTimeEditor()
258263
259264 array?.forEach {
260265
@@ -294,7 +299,6 @@ final class TSVSupport {
294299 private func registerKenzoMark( _ data: Data) {
295300
296301 let array = String(data: data, encoding: .utf8)?.components(separatedBy: "\n")
297- let store = LocalDataStore.oneTimeEditor()
298302
299303 array?.forEach {
300304
@@ -335,7 +339,6 @@ final class TSVSupport {
335339 private func registerDropShipHistory( _ data: Data) {
336340
337341 let array = String(data: data, encoding: .utf8)?.components(separatedBy: "\n")
338- let store = LocalDataStore.oneTimeEditor()
339342
340343 array?.forEach {
341344
--- a/KCD/UpdateQuestListCommand.swift
+++ b/KCD/UpdateQuestListCommand.swift
@@ -13,13 +13,14 @@ final class UpdateQuestListCommand: JSONCommand {
1313 override func execute() {
1414
1515 let store = ServerDataStore.oneTimeEditor()
16-
17- parameter["api_quest_id"]
18- .int
19- .flatMap(store.quest(by:))
20- .map {
21- $0.progress_flag = 0
22- $0.state = 1
16+ store.sync {
17+ self.parameter["api_quest_id"]
18+ .int
19+ .flatMap(store.quest(by:))
20+ .map {
21+ $0.progress_flag = 0
22+ $0.state = 1
23+ }
2324 }
2425 }
2526 }
--- a/KCD/UpdateSlotItemCommand.swift
+++ b/KCD/UpdateSlotItemCommand.swift
@@ -23,17 +23,19 @@ final class UpdateSlotItemCommand: JSONCommand {
2323
2424 let store = ServerDataStore.oneTimeEditor()
2525
26- guard let masterSlotItem = store.masterSlotItem(by: slotItemId) else {
26+ guard let masterSlotItem = store.sync(execute: { store.masterSlotItem(by: slotItemId) }) else {
2727
2828 return Logger.shared.log("MasterSlotItem is not found")
2929 }
3030
31- guard let new = store.createSlotItem() else {
31+ guard let new = store.sync(execute: { store.createSlotItem() }) else {
3232
3333 return Logger.shared.log("Can not create new SlotItem")
3434 }
3535
36- new.id = newSlotItemId
37- new.master_slotItem = masterSlotItem
36+ store.sync {
37+ new.id = newSlotItemId
38+ new.master_slotItem = masterSlotItem
39+ }
3840 }
3941 }