初めてのSwift5 その4

ファイル操作

初めてってわりにはかなりその2から関数ポインタもどきの話がでてきているので誰に合わせて書いているのかと申しますと私基準なわけです(爆)
ごめんなさい、コンピューター言語そのものの初心者向けではなかったね(;´д`)トホホ…

まあ私の備忘録のようなものであるので気にしないでね。

でも、深く考えずに書いて書いて書きまくれば覚えるものです。
私がプログラムを始めたのは小学生ぐらいのことでしたが、意味もわからずコードを模写して覚えたぐらいですから。
当時はBASICでゲーム作るみたいなもの。

私もSwif5初めて一週間も立っていないのでそのうち作りますか。
では、本題のプログラム言語触る上での基礎中の基礎、ファイル操作ですね。

画像1

何時も通りXCode立ち上げてFileメニューからNewを選択してProject選びます。

画像2

今回もSingle View Appを選択して右下のNextボタンを押します。

画像3

適当にProject名入れてNextボタンを押して適当な場所に作成します。

画像4

今回はこんな画面作りたいと思います。
シンプルですね〜。
テキスト表示するエリアと、テキスト入力するエリア、書き込みボタンと読込ボタンを今回はご用意致しました!
リストボックスより普通はこっちが先だよね(笑)

    var body: some View {
       //  縦に並べるぞっ!
       VStack {
           //  テキスト表示する部分
           Text(textToUpdate)
           //  テキストフィールド
           TextField("ここにテキストを入力してね", text: $inputText)
               //  外側のラインを丸めてみる
               .textFieldStyle(RoundedBorderTextFieldStyle())
           //  水平に揃えるよ
           HStack {
               //  ファイル書き込みボタン。テキストフィールドに書いた情報を書き込むよ
               Button(action: {
                   //  メンバー関数のWriteを呼び出して書き込むよ
                   if( self.Write(inSaveString: self.inputText) == true ){
                       self.textToUpdate = "ファイル書き込み成功"
                   }
                   else{
                       self.textToUpdate = "ファイルの書き込みに失敗しました"
                   }
               }) {
                   //  ボタンの名前
                   Text("書き込み")
               }
               //  ファイル読込ボタン。ファイルからテキスト読み込んでテキストフィールド
               //  に書き込むよ。
               Button(action: {
                   //  メンバー関数のReadを呼び出してファイルに保存したテキストを
                   //  メンバー変数であるreadTextにセットするよ
                   let readText = self.Read()
                   //  テキストフィールドにセットするよ
                   self.textToUpdate = "ファイルから読込 -> " + readText
               }) {
                   //  ボタンの名前
                   Text("読み込み")
               }
           }
       }

body部分はこんな感じ。
コメントに書いている通り。
新しくでてきたのは、

            //  テキストフィールド
           TextField("ここにテキストを入力してね", text: $inputText)
               //  外側のラインを丸めてみる
               .textFieldStyle(RoundedBorderTextFieldStyle())

この部分だね。
TextFiledってのは、その名前通り入力するテキストボックスだね。
これも以下のように選択してソースにドラッグすることでコードが作成されるよ。

画像5

右上の「+」ボタンを押して、でてくるホップアップウインドウの入力欄に「text」と入力して絞り込みます。
すると「Text Field」ってのがでてくるのでそれをソースコードに貼り付けるだけ。

textFileldStyleはディフォルトだと表示されていないので、自分で追加してね。
枠がないと見にくいから。

ファイルパスを取得する

やっていることはとてもシンプル。

    //  ユーザパスの取得
   func GetUserDocPath() ->
       URL{
           //  ユーザのドキュメントディレクトリのパスを取得してURL構造体で返すよ
           return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
   }

まずは、ファイル保存する場所を取得します。
iOS上のユーザが保管するドキュメントパスを取得します。
Unityでもあるよね、あれです。
見たまんまなんでコピペするぐらいでいいんじゃないかな?

ファイルの書き込み

    //  ファイルに書き込み
   //  戻り値はtrueは正常に書き込めた!
   //  falseは書き込み失敗!
   func Write(inSaveString: String) ->
       Bool{
           //  ユーザディレクトリパスを取得するよ
           let path = GetUserDocPath()
           //  ファイルパスの作成をするよ。
           //  ファイル名はtestfile.txtにするよ
           let filepath = URL(fileURLWithPath: "testfile", relativeTo: path)
               //  拡張子つけるよ。
               //  macは必要ない?人間が必要さ!
               .appendingPathExtension(".txt")
           //  文字列をUTF-8に変換するよ
           //  guardの説明として、処理が失敗したら途中で中断できるんだ。
           //  else文に入ってエラー処理ができるよ。
           //  try〜catchとの違いは途中中断するということ。
           //  try〜catchは囲い方次第では継続しようと思えばできるからね。
           guard let data = inSaveString.data(using: .utf8)
               else{
                   fatalError("UTF-8変換に失敗しました(;´д`)トホホ…")
           }
           do{
               //  ここでファイルに書き込むよ
               //  引数にファイルパスをセットするよ。
               //  他の言語と違って書き方が面白いよね。
               try data.write(to: filepath)
               //  正常を返すよ
               return true
           }
           catch{
               fatalError("書き込みに失敗しました")
           }
           //  ここにきた時点で失敗だよ
           return false
   }

ファイルの書き込みはこんな感じ。
コメントが長いだけで実際のコードはそうでもないよ。
これを特に難しいことしていないので、ソースのコメントみて貰えればいいレベル。

面白いのは、ファイルパスが何故かURLってところ。
インターネットの時代だからネットにつながること前提で作られた言語ってところなのかな。

ファイルの読み込み処理

    //  ファイル読込
   //  戻り値はString型で読み取ったテキスト戻すよ
   func Read() ->
       String{
           //  ドキュメントパス取得するよ
           let path = GetUserDocPath()
           //  ファイルパス作成するよ。このあたりは書き込みの時と同じ
           let filepath = URL(fileURLWithPath: "testfile", relativeTo: path).appendingPathExtension(".txt")
           
           do{
               //  ファイルからテキストデータ取り出すよ
               //  Data構造体として戻ってくるよ
               let savedData = try Data(contentsOf: filepath)
               //  Data構造体からテキスト取り出してUTF-8にエンコードするよ。
               if let savedString = String(data: savedData, encoding: .utf8){
                   //  読み取ったテキストを返すよ
                   return savedString
               }
           }
           catch{
               //  読み取り失敗したよ
               fatalError("だめだこりゃ!次いってみよう")
           }
           //  空の文字列を返すよ
           return ""
   }

ファイルの読込もシンプルです。
ただ、データの戻り値がDataという構造体で戻ってくるところ。
ここから、テキストを取り出す必要があるのでそのあたりが一寸手間かな。
なんか変数渡せば、セットして戻してくれるっての方が気楽なんだけどね。

実行してみよう

画像6

実行したらこんな感じの画面がでてきます。

画像7

こんな感じで、テキスト入力してみます。

書き込んでみよう

画像8

書き込み成功ってメッセージがでれば実際にファイルに書き込めています。
次にファイルからデータを取り出してみましょう。

読込しちゃうぞ!

画像9

読込ボタンを押すとこんな感じになれば正常に読み出させているよ。
とっても簡単でしょ。
なんか基本的なことだけど、データの保存と読み出しができるだけで広がっていく感じがするよね。
では!


今回の全ソースコード

//
//  ContentView.swift
//  HogeHoge3
//
//  Created by melon on 2020/05/08.
//  Copyright © 2020 melon-group. All rights reserved.
//

import SwiftUI

struct ContentView: View {
   //  メンバー変数(@Stateはオマジナイ!)
   @State var textToUpdate = "こんにちは、世界!"
   //  入力したテキストを格納する変数
   @State var inputText = ""
   
   var body: some View {
       //  縦に並べるぞっ!
       VStack {
           //  テキスト表示する部分
           Text(textToUpdate)
           //  テキストフィールド
           TextField("ここにテキストを入力してね", text: $inputText)
               //  外側のラインを丸めてみる
               .textFieldStyle(RoundedBorderTextFieldStyle())
           //  水平に揃えるよ
           HStack {
               //  ファイル書き込みボタン。テキストフィールドに書いた情報を書き込むよ
               Button(action: {
                   //  メンバー関数のWriteを呼び出して書き込むよ
                   if( self.Write(inSaveString: self.inputText) == true ){
                       self.textToUpdate = "ファイル書き込み成功"
                   }
                   else{
                       self.textToUpdate = "ファイルの書き込みに失敗しました"
                   }
               }) {
                   //  ボタンの名前
                   Text("書き込み")
               }
               //  ファイル読込ボタン。ファイルからテキスト読み込んでテキストフィールド
               //  に書き込むよ。
               Button(action: {
                   //  メンバー関数のReadを呼び出してファイルに保存したテキストを
                   //  メンバー変数であるreadTextにセットするよ
                   let readText = self.Read()
                   //  テキストフィールドにセットするよ
                   self.textToUpdate = "ファイルから読込 -> " + readText
               }) {
                   //  ボタンの名前
                   Text("読み込み")
               }
           }
       }
   }
   //  ユーザパスの取得
   func GetUserDocPath() ->
       URL{
           //  ユーザのドキュメントディレクトリのパスを取得してURL構造体で返すよ
           return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
   }
   //  ファイルに書き込み
   //  戻り値はtrueは正常に書き込めた!
   //  falseは書き込み失敗!
   func Write(inSaveString: String) ->
       Bool{
           //  ユーザディレクトリパスを取得するよ
           let path = GetUserDocPath()
           //  ファイルパスの作成をするよ。
           //  ファイル名はtestfile.txtにするよ
           let filepath = URL(fileURLWithPath: "testfile", relativeTo: path)
               //  拡張子つけるよ。
               //  macは必要ない?人間が必要さ!
               .appendingPathExtension(".txt")
           //  文字列をUTF-8に変換するよ
           //  guardの説明として、処理が失敗したら途中で中断できるんだ。
           //  else文に入ってエラー処理ができるよ。
           //  try〜catchとの違いは途中中断するということ。
           //  try〜catchは囲い方次第では継続しようと思えばできるからね。
           guard let data = inSaveString.data(using: .utf8)
               else{
                   fatalError("UTF-8変換に失敗しました(;´д`)トホホ…")
           }
           do{
               //  ここでファイルに書き込むよ
               //  引数にファイルパスをセットするよ。
               //  他の言語と違って書き方が面白いよね。
               try data.write(to: filepath)
               //  正常を返すよ
               return true
           }
           catch{
               fatalError("書き込みに失敗しました")
           }
           //  ここにきた時点で失敗だよ
           return false
   }
   //  ファイル読込
   //  戻り値はString型で読み取ったテキスト戻すよ
   func Read() ->
       String{
           //  ドキュメントパス取得するよ
           let path = GetUserDocPath()
           //  ファイルパス作成するよ。このあたりは書き込みの時と同じ
           let filepath = URL(fileURLWithPath: "testfile", relativeTo: path).appendingPathExtension(".txt")
           
           do{
               //  ファイルからテキストデータ取り出すよ
               //  Data構造体として戻ってくるよ
               let savedData = try Data(contentsOf: filepath)
               //  Data構造体からテキスト取り出してUTF-8にエンコードするよ。
               if let savedString = String(data: savedData, encoding: .utf8){
                   //  読み取ったテキストを返すよ
                   return savedString
               }
           }
           catch{
               //  読み取り失敗したよ
               fatalError("だめだこりゃ!次いってみよう")
           }
           //  空の文字列を返すよ
           return ""
   }
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView()
   }
}

この記事が気に入ったらサポートをしてみませんか?