独自Documentクラス
potatotips #49 (iOS/Android開発Tips共有会)
2018/3/13 Bitz Co., Ltd. 村上幸雄
• 埼玉県朝霞市でソフトハウスを起業。
• macOSやiOS、Androidのアプリケーション開発を主に請け負う。
• 自社アプリの製作。
• Twitter: @m_yukio
• Facebook: yukio.murakami
• GitHub: murakami
• http://www.bitz.co.jp/weblog/
BUKURO.swift
• macOS/iOS/watchOS/tvOSプログラマーのための勉強会。
初心者大歓迎。
• Cocoa勉強会 関東とMOSAの合同勉強会。
ほぼ毎月、池袋コワーキングスペース OpenOffice FOREST で開催。
• 申し込みは、connpassから。
https://cocoa-kanto.connpass.com/
• プログラマーが楽しくプログラミングできるようにサポートする場を
提供したい
• 勉強会に参加された方全員が学びを得られる様にサポートします!
• 勉強会に参加できない方にも学び得られるよう、勉強会の成果を可
能な限りアウトプットします!
• プログラマの拠り所となる場を目指します!
• PubHack.swiftは、飲み会イベントを通じて、
macOS/iOS/tvOS/watchOS/Androidエンジニアの人たちの交流を目的として
います。
• Cocoa勉強会 関東とMOSAの合同イベント。
• 申し込みは、connpassから。
https://cocoa-kanto.connpass.com/
Controller
View Model
Traditional version of MVC as a compound pattern
User action
Update
Update
Notify
Get changed state
Composite
Strategy
Observer
Controller
View Model
Cocoa version of MVC as a compound design pattern
User action
Update
Update
Notify
Command
Composite
Mediator
Strategy
Observer
NSViewControllerNSView
View Controller Model
NSDocument data
NSApplicationDelegate
ユーザー操作
event
更新
更新
更新
参照
通知
NSController
• iOS 5 からUIDocumentクラスが用意されたが、少々、オ
ーバースペック。
• 発表者は、以前から、独自のDocumentクラスを用意して
いた。
Documentクラスはモデル・コントローラ
class Document {
var version: String
private var _uniqueIdentifier: String
var uniqueIdentifier: String {
return _uniqueIdentifier
}
static let sharedInstance: Document = {
let instance = Document()
return instance
}()
init() {
let infoDictionary = Bundle.main.infoDictionary! as Dictionary
self.version = infoDictionary["CFBundleShortVersionString"]! as! String
self._uniqueIdentifier = ""
}
deinit {
}
func load() {
loadDefaults()
}
func save() {
updateDefaults()
}
private func clearDefaults() {
let userDefaults = UserDefaults.standard
if userDefaults.object(forKey: "version") != nil {
userDefaults.removeObject(forKey: "version")
}
if userDefaults.object(forKey: "uniqueIdentifier") != nil {
userDefaults.removeObject(forKey: "uniqueIdentifier")
}
}
private func updateDefaults() {
let userDefaults = UserDefaults.standard
var versionString: String = ""
if userDefaults.object(forKey: "version") != nil {
versionString = userDefaults.object(forKey: "version") as! String
}
if versionString.compare(self.version) != .orderedSame {
userDefaults.setValue(self.version, forKey: "version")
userDefaults.synchronize()
}
var uniqueIdentifier: String = ""
if userDefaults.object(forKey: "uniqueIdentifier") != nil {
uniqueIdentifier = userDefaults.object(forKey: "uniqueIdentifier") as! String
}
if uniqueIdentifier.compare(self.uniqueIdentifier) != .orderedSame {
userDefaults.setValue(self.uniqueIdentifier, forKey: "uniqueIdentifier")
userDefaults.synchronize()
}
}
private func loadDefaults() {
let userDefaults = UserDefaults.standard
var versionString: String = ""
if userDefaults.object(forKey: "version") != nil {
versionString = userDefaults.object(forKey: "version") as! String
}
if versionString.compare(self.version) != .orderedSame {
/* バージョン不一致対応 */
clearDefaults()
_uniqueIdentifier = UUID.init().uuidString
}
else {
/* 読み出し */
if userDefaults.object(forKey: "uniqueIdentifier") != nil {
_uniqueIdentifier = userDefaults.object(forKey: "uniqueIdentifier") as! String
}
}
}
private func modelDir() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if paths.count < 1 {
return ""
}
var path = paths[0]
path = path.appending(".model")
return path
}
private func modelPath() -> String {
let path = modelDir().appending("/model.dat")
return path;
}
}
シングルトンを使わない例
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
public let document = Document()
:
}
let delegate = UIApplication.shared.delegate as! AppDelegate
let document = delegate.document
print(document)
過去の反省点
• 当初はコントローラという意識が低く、ロジックをDocument利
用側に持たせてしまった。
• Documentを意識するのはいいのだが、愚直にDocumentクラス
を用意して満足してしまった。
概念なので、用途に合わせて分割するとか、柔軟にしていれば。
• 利用しやすさから、シングルトンを選択したが。
• 単体テストを書いて、Documentがスカスカなら、疑問を持たな
いと。過去の自分。

独自Documentクラス