//tips
//swift振り返り
現在SwiftUITODOリストの保存の部分でエラーが出ているのでそこから見直す。
エラー内容は下記になり、
Value of optional type '[Any]?' must be unwrapped to refer to member 'count' of wrapped base type '[Any]'
Chain the optional using '?' to access member 'count' only for non-'nil' base values
Force-unwrap using '!' to abort execution if the optional value contains 'nil'
List内のfor eachの箇所が該当する。
import SwiftUI
struct ContentView: View {
@State var tasks = [String]()
//@State var tasks = [String]()
@State var newtask = ""
var userDefaults = UserDefaults.standard
var body: some View {
VStack {
NavigationView{
List{
ForEach(0..<tasks.count, id: \.self) { index in
TextField("PlainTextFieldStyle", text: self.$tasks[index] , onCommit: {
UserDefaults.standard.set(self.tasks, forKey: "キー")
})
}
//Text(self.tasks[task])}
.onDelete(perform: rowRemove)
}
.navigationBarTitle("Menu")
let tasks = userDefaults.array(forKey: "キー")
}
HStack {
Spacer()
Button(action: {
self.tasks.append(self.newtask)
self.newtask = ""
//self.tasks = createTasks()
}) {
Image(systemName:"pencil.tip.crop.circle.badge.plus")
.resizable()
.foregroundColor(/*@START_MENU_TOKEN@*/.black/*@END_MENU_TOKEN@*/)
.padding(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
.scaledToFit()
.frame(width: 80, height: 80)
}
}
}
}
func rowRemove(offsets: IndexSet) {
tasks.remove(atOffsets: offsets)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
まずはエラーの原因となった追加したUserDefaults.standardについて確認していく。
https://developer.apple.com/documentation/foundation/userdefaults
UserDefaultsはclass UserDefaults : NSObjectと定義されることがわかり、下記疑問が湧いたので順に確認していく。
・構造体の中にclassを作成するとは、どのような意味を持つのか。
構造体は値型、classは参照型というが、イメージとしては、構造体は、対象に対して、過去に撮影した写真のように普遍的な値に対して使用される一方で、classは、対象がその瞬間のリアルタイムに行っていることを覗き見る感じで常に最新状態を確認することと言える。
対象を人間とした場合、人間が他の動物に変わることはないので、構造体として登録しておき、現在何を行っているかはclassでアクセスパスを繋いでおくことで随時確認するようなもの。
UserDefaultsは変更が繰り返される値を保存する必要があるので構造体ではなく、classでの定義が妥当と言える。
structはデフォルトで不変であるが、一部変数varなどで、可変の状態を持つことができる。これは、structが変更に対して敏感で、意図しない内部状態を変更することを防ぐ機能と言える。
対象が代入や関数に渡した際に参照されるのではなく、コピーされることが期待されている時に構造体を使うことが適切と言える。
とするならば、 TextFieldを用いて、随時内容を変更できるようにする構成は構造体ではなくclassの方が適当だと考えられる。修正の余地あり。
・プロトコルにNSObjectを持つことで、コードに必要なことは何か
NSObjectプロトコルは、Objective-Cに基づくもので、C言語をベースにしたオブジェクト指向の言語として、Swiftが誕生する前は、この言語でiosのアプリが記述されていた。
表記が独特でわかりづらいが、過去に蓄積されたライブラリ類など膨大な資産を持つため、いまだに使用されている。
一方、Swiftを利用することで、コードを短縮できたり、nilチェックがしっかりできるという強みがある。
ただ、歴史が浅いのでデータベースは充実しておらず、Objective-Cの資産を使いまわさずを得ないことからNSObjectがSwiftに登場しているのである。
NSObjectは、オブジェクトの動的な振る舞いを助けるシステムで、オブジェクトの生成、メモリ領域の管理、メッセージに対応するメソッドの探索などを行うものである。
https://developer.apple.com/documentation/objectivec/nsobject
https://blog.fenrir-inc.com/jp/2011/09/nsobject-protocol.html
//UserDefaultsで保存できる型と使用するメソッド
UserDefaultsで保存できる型と使用するメソッドが決まっているようで、配列型の場合は[Any]として、保存時「set:forKey」: 、参照時の処理「array:forKey:」となるよう。
下記を参考にし、UserDefaultsの使用確認用アプリを別途作成してみる。