
데이터를 저장하기 위해서, storage layer가 MVC 아키텍처에 추가된다.
controller object를 통해 storage layer에 접근한다.
앱의 데이터를 저장하는 방법
앱이 시작되면 저장된 데이터를 가져와 사용하고, 앱의 데이터가 수정되거나 앱이 꺼질 때 데이터가 저장된다.
Codablemodel object가 인코딩, 디코딩되기 위해선 반드시 Codable protocol을 차용해야한다.
Codable protocol을 차용하기 위해서 Decoder와 Encoder를 파라미터로 쓰는 init(from: Decoder), encode(to: Encoder) method를 작성해야한다.
만약 object의 property가 이미 Codable을 차용한 타입들인 경우, 위의 method들을 작성하지 않아도 된다.
// String, Date는 Codable을 차용한 Swift type이므로
// protocol의 요구 method들을 차용하지 않아도 된다.
struct Note: Codable {
let title: String
let text: String
let timestamp: Date
}
encode(_:)Encoder object
Data object를 반환
let newNote = Note(title: "Grocery run", text: "Pick up mayonnaise, mustard, lettuce, tomato, and pickles.", timestamp: Date())
let propertyListEncoder = PropertyListEncoder()
// try? 사용을 통해 encode(_:)`가 에러를 던지는 대신 optional `Data`를 반환
if let encodedNote = try? propertyListEncoder.encode(newNote) {
// Data object에 저장된 bytes
print(encodedNote)
}
Data
- byte형식으로 데이터를 표현하는 Swift structure
- 파일로부터 읽고 쓰는 역할의 instance method 제공
decode(_:from:)Decoder object
parameter: Data object
return: Codable object instance
let propertyListDecoder = PropertyListDecoder()
// Note.self: 특정한 Note object가 아닌 실제 Note Swift type
if let decodedNote = try? propertyListDecoder.decode(Note.self, from: encodedNote) {
print(decodedNote)
}
많은 OS에서 앱은 디스크 어디에서나 파일들을 읽고 쓸 수 있는 권한을 가진다.
ex)
Numbers: 모든 폴더로부터 spreadsheet 열기 가능
Web browser: 모든 directory에 파일 쓰기 가능
이러한 행동은 불안정한 앱이 앱과 관련없는 중요한 데이터를 삭제할 수도 있는 본질적 위험을 가진다.
ex) 음악 파일을 수정하는 앱이 모든 사진을 삭제할 수 있음
iOS App은 sandbox model을 사용하여 프로그램에 그들이 생성하거나 접근을 요청받은 자원에 대한 접근 권한을 부여한다.
각 앱은 자신의 sandbox(생성, 수정, 삭제가 가능한 자신의 환경)를 가지고, sandbox 밖의 자원은 접근할 수 없다.
OS가 앱 바깥의 자원에 접근을 허락하더라도, app은 사용자로부터 명백한 허가를 받을 때만 가능하다.
앱은 데이터를 저장할 수 있는 몇몇개의 디렉토리를 가진다.
ex) Document directory: 앱과 관련된 정보를 저장하거나 수정할 수 있는 곳
디렉토리의 파일 경로는 중요한 보안 이슈를 막기 위해 앱이 메모리에 로딩될 때마다 바뀔 수 있다.
Foundation framework는 디스크의 파일과 상호작용하는데 쓰이는 FileManager class를 가진다.
FileManagerFoundation framework에서 앱에 directory 접근 권한을 주고, 해당 directory의 파일을 읽고 쓸 수 있게하는 함수를 갖고있는 classurls(for:in:)// .userDomainMask: 사용자의 앱과 관련된 모든 데이터를 갖고있는 home 폴더
// Document directory는 1개뿐이므로 반환값에서 첫번째 값만 가져온다.
// ~~~/Documents/
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
appendingPathComponent() & appendingPathExtension()// ~~~/Documents/notes_test.plist
let archiveURL = documentsDirectory.appendingPathComponent("notes_test").appendingPathExtension("plist")
write(to:options:)Data가 담긴 파일을 생성하는 throwing function// .noFileProtection: 덮어쓰기 허락
try? encodedNote?.write(to: archiveURL, options: .noFileProtection)
init(contentsOf:)Data object를 초기화하는 throwing initializerlet propertyListDecoder = PropertyListDecoder()
if let retrievedNoteData = try? Data(contentsOf: archiveURL),
let decodedNote = try? propertyListDecoder.decode(Note.self, from: retrievedNoteData) {
print(decodedNote)
}
let note1 = Note(title: "Note One",
text: "This is a sample note.", timestamp: Date())
let note2 = Note(title: "Note Two", text: "This is another sample
note.", timestamp: Date())
let note3 = Note(title: "Note Three", text: "This is yet another
sample note.", timestamp: Date())
let notes = [note1, note2, note3]
let documentsDirectory =
FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
let archiveURL =
documentsDirectory.appendingPathComponent("notes_test")
.appendingPathExtension("plist")
let propertyListEncoder = PropertyListEncoder()
let encodedNotes = try? propertyListEncoder.encode(notes)
try? encodedNotes?.write(to: archiveURL,
options: .noFileProtection)
let propertyListDecoder = PropertyListDecoder()
if let retrievedNotesData = try? Data(contentsOf: archiveURL),
let decodedNotes = try?
propertyListDecoder.decode(Array<Note>.self,
from: retrievedNotesData) {
print(decodedNotes)
}
model object는 Codable protocol을 차용해야 한다.
파일로부터 읽고 쓰는 역할을 model의 static method로 작성한다.
ex) saveToFile(), loadFromFile()
model data를 추가, 제거, 수정할 때마다 file을 update한다.
loadFromFile()
앱이 시작되는 초반에 디스크로부터 데이터 로딩
AppDelegate 또는 first view controller의 viewDidLoad() method 내부에 수행