SwiftUI 3-3-2. JSONファイルを読み込む

前節で, ストレージにJSONファイルを書き込みました. 今回はファイルが正しく書き込みされているか確認する意味も込めてストレージのファイルの読み込み処理を追加します. 前回のコードに続けて書く PhotoDataの中に load( path: )関数 を作成する ファイルが存在するかを FileManager.default.fileExists( atPath: ) で確認 ファイルがない場合は空の配列を返すようにする URL( fileURLWithPath:path ) で読込パスをURLに変換する try! Data( contentOf:url ) でファイルを読み込む 3-02で作成済みの PhotoData.parse( json: ) で[PhotoData]に変換して返す コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import Foundation struct PhotoData : Identifiable, Codable, Equatable { var id:String = UUID().uuidString var type:String var imageName:String var title:String var message:String static func parse( json:Data ) -> [PhotoData] { do { let photos = try JSONDecoder().decode( [PhotoData].self, from:json ) return photos } catch { print( "JSONをPhotoData配列に変換できませんでした." ) print( error.localizedDescription ) return [] } } static func load( path:String ) -> [PhotoData] { if FileManager.default.fileExists( atPath:path ) == false { return [] } let url = URL( fileURLWithPath: path ) let user_json = try! Data( contentsOf:url ) return PhotoData.parse( json:user_json ) } static func save( path:String, data:[PhotoData] ) { let user_json = try! JSONEncoder().encode( data ) let url = URL( fileURLWithPath:path ) try! user_json.write( to:url, options:[.atomic] ) } } これでストレージに保存したJSONを読み込むことができるようになりました. ContentViewで使ってみます. ...

2023-03-28 · 2024-07-28

SwiftUI 3-3-3. JSONファイル読み書きコードの改善・例外の対応

3-3-1でJSONファイルの書き出し, 3-3-2でJSONファイルの読み込みを扱いました. これらのコードを使いやすくするため, 例外の処理などを加えて改善します。 まず, JSON保存側の処理の見直しです. PhotoData.save( path: data: )関数 を例外に対応させる 基本的に3-2-3と同様の作業を行う try! を try にする do { ~ } catch { ~ } を使って例外発生を捉える catch内で例外時の処理を記述する コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import Foundation struct PhotoData : Identifiable, Codable, Equatable { var id:String = UUID().uuidString var type:String var imageName:String var title:String var message:String static func parse( json:Data ) -> [PhotoData] { do { let photos = try JSONDecoder().decode( [PhotoData].self, from:json ) return photos } catch { print( "JSONをPhotoData配列に変換できませんでした." ) print( error.localizedDescription ) return [] } } static func load( path:String ) -> [PhotoData] { if FileManager.default.fileExists( atPath:path ) == false { return [] } let url = URL( fileURLWithPath: path ) let user_json = try! Data( contentsOf:url ) return PhotoData.parse( json:user_json ) } static func save( path:String, data:[PhotoData] ) { do { let user_json = try JSONEncoder().encode( data ) let url = URL( fileURLWithPath:path ) try user_json.write( to:url, options:[.atomic] ) } catch { print( "ファイルの保存に失敗しました" ) print( error.localizedDescription ) } } } これで書き込みに失敗したとき, エラーがコンソールに表示されます. ...

2023-03-28 · 2023-03-30

SwiftUI 3-3-4. JSONファイルの削除

JSONの書き出し, JSONの読み込みはできました. ストレージに書き出したファイルは残るようになりましたが, 書き出したJSONファイルが不要になることもあるでしょう. 今回は作成したJSONファイルを削除する処理を追加します. PhotoDataに clear( path: )関数 を追加する FileManager.default.removeItem( atPath: ) でファイルを削除する do { ~ } catch { ~ } を使い例外に対応した処理も書く コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import Foundation struct PhotoData : Identifiable, Codable, Equatable { var id:String = UUID().uuidString var type:String var imageName:String var title:String var message:String static func parse( json:Data ) -> [PhotoData] { do { let photos = try JSONDecoder().decode( [PhotoData].self, from:json ) return photos } catch { print( "JSONをPhotoData配列に変換できませんでした." ) print( error.localizedDescription ) return [] } } static func load( path:String ) -> [PhotoData] { if FileManager.default.fileExists( atPath:path ) == false { return [] } do { let url = URL( fileURLWithPath: path ) let user_json = try Data( contentsOf:url ) return PhotoData.parse( json:user_json ) } catch { print( "ファイルの読み込みに失敗しました" ) print( error.localizedDescription ) return [] } } static func save( path:String, data:[PhotoData] ) { do { let user_json = try JSONEncoder().encode( data ) let url = URL( fileURLWithPath:path ) try user_json.write( to:url, options:[.atomic] ) } catch { print( "ファイルの保存に失敗しました" ) print( error.localizedDescription ) } } static func clear( path:String ) { do { try FileManager.default.removeItem( atPath:path ) } catch { print( "ファイルの削除に失敗しました" ) print( error.localizedDescription ) } } } clear関数が動作しているかを確認するため, 一時的なコードをContentViewに追加してみます. ...

2023-03-28 · 2024-07-28

SwiftUI 3-3-5. JSONファイルの有無とAppの挙動・初期化

前回までで、JSONファイルの書き出し・読み込み・削除ができるようになりました. これらを踏まえ,Appの挙動に視点を移して考えてみます. Appはインストールして初めて立ち上げる初回起動などがあります. 初回起動する前はもちろんJSONファイルなどは書き出していないのでファイルは存在しません. ファイルが存在しないときどう処理するかなどを考える必要があります. ファイルに限りませんが, 初回起動はAppが正しく動作するための準備処理を書くことが多いです. 今回は Documentsフォルダにuser_photos.jsonがないとき, 初期化処理として **
Resources/photos.jsonを読み込む** ようにPhotoData.load関数をカスタマイズしてみます. 前回のコードに続けて書く PhotoDataの load( path: )関数 でファイルが見つからなかったときに処理を追加する 初期データとしてResources/photos.jsonを読み込む コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import Foundation struct PhotoData : Identifiable, Codable, Equatable { var id:String = UUID().uuidString var type:String var imageName:String var title:String var message:String static func parse( json:Data ) -> [PhotoData] { do { let photos = try JSONDecoder().decode( [PhotoData].self, from:json ) return photos } catch { print( "JSONをPhotoData配列に変換できませんでした." ) print( error.localizedDescription ) return [] } } static func load( path:String ) -> [PhotoData] { if FileManager.default.fileExists( atPath:path ) == false { print( "ファイルがみつかりませんでした, Resourceからファイルを読み込みます" ) let resource_json = Resource.load( name:"photos", ext:"json" ) return PhotoData.parse( json:resource_json ) } do { let url = URL( fileURLWithPath: path ) let user_json = try Data( contentsOf:url ) return PhotoData.parse( json:user_json ) } catch { print( "ファイルの読み込みに失敗しました" ) print( error.localizedDescription ) return [] } } static func save( path:String, data:[PhotoData] ) { do { let user_json = try JSONEncoder().encode( data ) let url = URL( fileURLWithPath:path ) try user_json.write( to:url, options:[.atomic] ) } catch { print( "ファイルの保存に失敗しました" ) print( error.localizedDescription ) } } static func clear( path:String ) { do { try FileManager.default.removeItem( atPath:path ) } catch { print( "ファイルの削除に失敗しました" ) print( error.localizedDescription ) } } } これで初回起動時のみResouces/photos.jsonの中身が読まれます.ですがこれは初期化の一例に過ぎず色々な処理がありうるでしょう. ただ, このようにAppの初回起動時に何かデータを用意したい時などはデータの有無によって特定のファイルを読むといったことはAppの利便性向上に役立ちます. ...

2023-03-28 · 2024-07-28

SwiftUI 3-1-2. データを追加する

前回でデータの変更が可能になりました. 今回はデータを追加する方法を紹介します. まずデータを増やすためのボタンを設置します. ボタンの設置のためリストのヘッダをカスタマイズします. ContentViewを開き, List内のSectionの書き方を変更する Sectionの( “景色写真” )を消す Section { ~ } の後に header: { Viewデザイン } を追加する 「景色写真」の文字は, headerの中のTextで表示する ヘッダに文字だけでなく, 他のView部品を入れられるようになる コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import SwiftUI struct ContentView: View { @State var photos:[PhotoData] = [ PhotoData( type:"景色", imageName:"CC0_01", title:"景色1", message:"森と山です" ), PhotoData( type:"景色", imageName:"CC0_04", title:"景色2", message:"きれいな湖です" ), PhotoData( type:"景色", imageName:"CC0_06", title:"景色3", message:"フィレンツェです" ), PhotoData( type:"景色", imageName:"CC0_07", title:"景色4", message:"ひまわりです" ), PhotoData( type:"景色", imageName:"CC0_10", title:"景色5", message:"海に日が沈みます" ) ] var body: some View { List { Section { ForEach( $photos ) { bind_photo in NavigationLink( destination:{ SceneryDetail( data:bind_photo ) }, label: { SceneryRow( data:bind_photo.wrappedValue ) }) } } header: { HStack { Text( "景色写真" ) Spacer() Image( systemName:"plus" ) .font( .title2 ) .foregroundStyle( Color.accentColor ) } } } .listStyle( .grouped ) .navigationTitle( "写真集" ) } } 結果 ...

2023-03-23 · 2024-07-28

SwiftUI 3-1-3. データを削除する

前回, Viewからデータを追加できる例を紹介しました. 今回は逆にデータを削除する例を紹介します. これはリストの機能を使うとシンプルに実現できます. ContentViewのSectionのForEachに.onDelete { … }を追加する ForEachで繰り返し表示している項目を右スライドできるようになる onDelete内で photos.remove( atOffsets: ) を呼ぶ 該当する位置のデータを削除できる( onDeleteのindex_setに該当のデータ位置が入っている ) Photosの中身も減っているのでprintなどで確認すると良い コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import SwiftUI struct ContentView: View { @State var photos:[PhotoData] = [ PhotoData( type:"景色", imageName:"CC0_01", title:"景色1", message:"森と山です" ), PhotoData( type:"景色", imageName:"CC0_04", title:"景色2", message:"きれいな湖です" ), PhotoData( type:"景色", imageName:"CC0_06", title:"景色3", message:"フィレンツェです" ), PhotoData( type:"景色", imageName:"CC0_07", title:"景色4", message:"ひまわりです" ), PhotoData( type:"景色", imageName:"CC0_10", title:"景色5", message:"海に日が沈みます" ) ] var body: some View { List { Section { ForEach( $photos ) { bind_photo in NavigationLink( destination:{ SceneryDetail( data:bind_photo ) }, label: { SceneryRow( data:bind_photo.wrappedValue ) }) } .onDelete { index_set in photos.remove( atOffsets:index_set ) } } header: { HStack { Text( "景色写真" ) Spacer() Image( systemName:"plus" ) .font( .title2 ) .foregroundStyle( Color.accentColor ) } } } .listStyle( .grouped ) .navigationTitle( "写真集" ) } } .onDeleteを追加すると, Listの項目を右スライドできるようになります. 右スライドして出てきたDeleteを押すと, データが消せます. ...

2023-03-23 · 2024-07-28

SwiftUI 3-2-1. JSONから構造体への変換とファイルのバンドル

前節ではSwift内のデータの編集を紹介しました. この節では, これまでSwiftに直接書き込んでいたリストのデータをファイルから読み込んでみます。多くのアプリケーションは用いるデータを外部から得てくることがほとんどです. 今回はその1例としてJSONファイルを読み込んでリスト表示にしてみます. SFSwiftUI03-2.swiftpm を開く (サンプルコード) 03-1を完了したコードと同様 photos配列を空っぽにしている 後で読み込むためのJSONファイルが入っている データがないため, プレビューも何もない状態から始まることを確認 コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import SwiftUI struct ContentView: View { @State var photos:[PhotoData] = [] var body: some View { List { Section { ForEach( $photos ) { bind_photo in NavigationLink( destination:{ SceneryDetail( data:bind_photo ) }, label: { SceneryRow( data:bind_photo.wrappedValue ) }) } .onDelete { index_set in photos.remove( atOffsets:index_set ) } } header: { HStack { Text( "景色写真" ) Spacer() Image( systemName:"plus" ) .font( .title2 ) .foregroundStyle( Color.accentColor ) } } } .listStyle( .grouped ) .navigationTitle( "写真集" ) } } 初期状態 ...

2023-03-23 · 2024-07-28

SwiftUI 3-0-1. 3章の目標・進め方・サンプルコード

3章の進め方と目標 SwiftUI入門の3章は、まず1,2章をご覧いただいた上で進めることをお勧めします。 この章では以下のことを目標にしています。 2章5節で紹介したデータとビューの連携をさらに深める データの変更とビューの更新 JSON形式のデータを扱い,ビューで表現する サーバからデータを取得して,ビューで表現する 触れていく中で1つずつ理解できるよう工夫しています。1節目から進めていただけると幸いです。 サンプルコード 各ページから 1,2章と同様サンプルコードが必要になるタイミングで各ページに用意しています。必要に応じて入手してご利用ください。 Githubから Githubにも公開しています。一括してサンプルコードが欲しい方はこちらから取得してください。 https://github.com/wdkk/ShigeFuji-SwiftUI-swiftpm

2023-03-09 · 2023-03-30

SwiftUI 3-1-1. 変更可能なデータを準備する

この節では2-5で作成したリストのプログラムをベースに, Appのデータを後から変更できるようにします. まず2-5までのコード作成が完了した状態をつくります. SFSwiftUI03-1.swiftpm を開く (サンプルコード) 2-5を完了させるとできあがるコードとほぼ同様のものとなる ContentView : リスト画面 MyApp : 起動時の処理 PhotoData : 写真データ構造体 SceneryDetail : 写真データの詳細画面 SceneryRow : 写真リストのビュー ただし簡単のため, FoodAndDrink関連のデータと画面は除いている コードの詳細は2-5-1を参考にしてください. 実行すると次のような画面の動きになります. 起動したとき リスト項目を押して画面遷移 今回は, このコードをスタートにまずデータを変更できるように変更します. ContentViewを開き, var photos を @State var photos に変更する ForEachに使うデータをphotosから$photosに変更する 繰り返しで得られるデータがBinding型になる. 変数名も bind_photo にしてわかりやすくする SceneryDetailにbind_photoを渡す SceneryRowにbind_photoを直接渡せないため, bind_photo.wrappedValue を指定する. Bindingで包んだ中身(PhotoData型)を取り出せる コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import SwiftUI struct ContentView: View { @State var photos:[PhotoData] = [ PhotoData( type:"景色", imageName:"CC0_01", title:"景色1", message:"森と山です" ), PhotoData( type:"景色", imageName:"CC0_04", title:"景色2", message:"きれいな湖です" ), PhotoData( type:"景色", imageName:"CC0_06", title:"景色3", message:"フィレンツェです" ), PhotoData( type:"景色", imageName:"CC0_07", title:"景色4", message:"ひまわりです" ), PhotoData( type:"景色", imageName:"CC0_10", title:"景色5", message:"海に日が沈みます" ) ] var body: some View { List { Section( "景色写真" ) { ForEach( $photos ) { bind_photo in NavigationLink( destination:{ SceneryDetail( data:bind_photo ) }, label: { SceneryRow( data:bind_photo.wrappedValue ) }) } } } .listStyle( .grouped ) .navigationTitle( "写真集" ) } } @Stateをつけたことでphotosは値を変更できるようになりました. しかしphotosが [PhotoData]型からState<[PhotoData]>型に変わった ことで, ForEachで得られる値も Binding型 になりました. ...

2023-03-09 · 2024-06-06

SwiftUI 2-5-1. リストとNavigationの連携

前節ではデータとViewを独立させ, 繰り返し処理(Foreach)を使ってデータの数の分だけリストを作ることを行いました。データは 構造体 でわかりやすく整理した上で配列に必要な分だけデータを追加することができるようになりました。今節では前節のデータ構造を用いながら,2-1節で触れた画面遷移を連携させていきます。 SFSwiftUI02-5.swiftpm を開く (サンプルコード) 中身が2-4を終えた段階と同じであることを確認する MyApp.swiftを開き, NavigationStack を追加, ContentViewを包む コード1 1 2 3 4 5 6 7 8 9 10 11 12 13 import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { NavigationStack { ContentView() .preferredColorScheme( .light ) } } } } 次にContentViewのナビゲーション設定を行います。 ...

2023-01-22 · 2025-07-09