UIDocumentを用いてデータを保存しているのですが、保存後に処理をしたり、読み込み後に処理をする場合に上手くいかないということが多々あったのでメモ。
UIDocumentを用いるデータの保存の仕方についても記事を書いていますのでそちらもどうぞ。
【Swift】NSData/NSFileWrapperを利用してデータを保存する
まずはデータを読み込む時ですが
//適当にPath名
struct USDocInfo{
static let NAME = "uruly"
static let ECTENSION = "xyz"
static var LOCAL_DOCUMENTS_PATH:String? = nil
}
//アプリのサンドボックスのパスを格納する変数
var localDocumentsPath:String{
if let dir = USDocInfo.LOCAL_DOCUMENTS_PATH{
return dir
}else {
let dir = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)[0] + "/"
USDocInfo.LOCAL_DOCUMENTS_PATH = dir
return dir
}
}
func readData(){
let filePath = localDocumentsPath + USDocInfo.NAME + "." + USDocInfo.ECTENSION
let fileUrl = NSURL(fileURLWithPath:filePath)
//ファイルの存在チェック
let isFileExists = NSFileManager.defaultManager().fileExistsAtPath(filePath)
let document = USDocument(fileURL:fileUrl)
if isFileExists{
document.openWithCompletionHandler({ (success:Bool) -> Void in
if success{
print("ファイルを開きました")
}else {
print("開けなかったよ")
}
})
}else{
print("ファイルはありません")
}
}
こんな感じ。UIDocumentクラスを用いて作成したUSDocumentってやつの中身は上記記事に書いてありますのでここでは割愛。
問題はここ
document.openWithCompletionHandler({ (success:Bool) -> Void in
if success{
print("ファイルを開きました")
}else {
print("開けなかったよ")
}
})
ファイルを開くのに成功した時に、ここで色々と処理をさせたいのですが、このopenWithCompletionHandlerというやつは非同期処理で行います。
つまり、mainスレッドでは別の処理を行っています。
readDataに読み出しの内容が書かれていますが、
override func viewDidLoad() {
super.viewDidLoad()
readData()
//こっちが先に呼び出される
print("hoge")
}
このように呼び出した場合に、「ファイルを開きました」→「hoge」という順ではなく
「hoge」→「ファイルを開きました」の順になるでしょう。
こうなると厄介ですよね。
ならばopenWithCompletionHandlerに直接処理を書いちゃえばいいじゃない。というわけなんですが
この中でUIを変えようとすると
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes
なんてエラーが出まくります。
メインスレッド以外でレイアウト変えるなよ〜今は大丈夫だけど今後クラッシュするよ〜みたいな感じ(?)
なのでこの中の処理が終わった後にメインスレッドで処理を行えばOKなはず・・・
ってなわけで
document.openWithCompletionHandler({ (success:Bool) -> Void in
if success{
print("ファイルを開きました")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("この中でUI変更等の処理を行う")
})
}else {
print("開けなかったよ")
}
})
dispatch_asyncでメインスレッドに戻してやると上手くいきました。
saveToURLも同様に
document.saveToURL(fileUrl, forSaveOperation: .ForCreating, completionHandler: {(success:Bool) -> Void in
if success{
print("Documentを保存しました")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("この中でUI変更等の処理を行う")
})
}else {
print("Documentを保存できませんでした")
}
})
これで非同期処理が終わった後に上手く処理できると思います。。
アプリ進捗
メモリ管理もそこそこに、あとはチュートリアル作成とアイコン作成のみといった感じなんですが、うーむアプリ名すら未だ決定していないという状況。細かいエラーが湧いて出てくる恐怖もありデバッグデバッグ。いや1ヶ月で作る予定がもう早2ヶ月経ってしまっている。びっくり。
メモリ管理がめちゃくちゃ大変。
そしてビックリしたのが、iPhone4Sで実機テストを行っているのですが、なんとメモリ0.5GB!5から1GBで今じゃ2GBだっていうのに!
ま、まぁ4Sで落ちないアプリならばきっと大丈夫と言い聞かせて・・・
とにかくちゃっちゃと終わらせてまずはリリースしてみてから考えないとダメな感じですね。細かいエラーも見つけられた範囲では片付けられているはずなので。。。
2日ほど腹痛で死んでましたが、大分元気になったので今週中に申請します・・・・。がんばります。。。。
Additional Notes追記
クロージャを使いましょうね!
【Swift 3】関数内で非同期処理を行った後の値を返り値としたかった話【クロージャ】を参考にしてください!
これを使える今と昔ではできることが世界が全然違うよ本当に・・・
コメントはありません。
現在コメントフォームは工事中です。