반응형
250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- UISlider
- 내일배움캠프
- 플러터
- 계산기만들기
- 커맨드라인툴
- Xcode
- Wil
- ListTile
- 날짜처리
- Storyboard
- pull down button
- 오토레이아웃
- 스파르타코딩클럽
- 알고리즘
- 개발자
- DART
- 메모장만들기
- strikeThrough
- pop up button
- Swift
- Storyboard Reference
- 내배캠
- 앱개발
- userdefaults
- attributedText
- todolist
- URLSession
- ios
- Flutter
- 코드스니펫
Archives
- Today
- Total
이리메라 갖다가
[Swift] 메모 앱 만들기 심화 (1) : UserDefaults, URLSession 적용하기 본문
728x90
반응형
어제 배운 UserDefaluts와 URLSession을 기존에 작업한 메모앱에 적용을 해보려고 한다.
UserDefaluts
- 기존에 작업한 모델을 최대한 변경하지 않고 유지하는 방법을 고민
- 고민의 흔적
- 1) Memo 값 전체를 UserDefaults로 설정하는 방법
- 2) 기존 함수 내 실행코드에 UserDefaults를 적용하는 방법
- 첫번째 방법은 반복되는 코드가 발생하고 쓸데없이 길어져 가독성이 떨어지는 문제가 있음
- 두번째 방법은 기존에 있는 코드를 그대로 활용하면 되고, 추가되는 코드가 적기 때문에 가독성이 높음
- 메모 생성/수정/삭제/로드(CRUD) 함수 내부 실행코드 마지막에 UserDefaults로 해당 데이터를 저장하는 코드를 추가하는 방법으로 구현
- 그리고 클래스로 구현한 Memo를 구조체로 변경(딱히 클래스여야 할 이유가 없음.. 상속을 받는 것도 아니고.. 그렇다고 Memo를 바로 쓰는 것도 아니라서 변경함)
Memo 구조체
import Foundation
struct Memo: Codable {
var content: String // 메모 타이틀
var isCompleted: Bool // 완료여부
var insertDate: Date // 작성일
var targetDate: Date? // 목표일자
var priority: String? // 중요도(우선순위)
var category: String? // 카테고리
var progress: Int? // 진행율
}
MemoManager 클래스
class MemoManager {
static let shared = MemoManager() // 싱글톤 패턴
private var userDefaults = UserDefaults.standard // 접근 제어
var memoList: [Memo] = []
// (접근 제어) 초기화 : UserDefaults를 통해 메모 불러오기 - (1)
private init() {
if let data = userDefaults.data(forKey: "MemoList"),
let saveMemoList = try? JSONDecoder().decode([Memo].self, from: data) {
memoList = saveMemoList
}
}
// 메모 추가
func addMemo(content: String, isCompleted: Bool, priority: String?, category: String?, progress: Int?) {
...
// 기존 코드
saveMemoListToUserDefaults()
}
// 메모 수정
func updateMemo(at index: Int, newContent: String, isCompleted: Bool, insertDate: Date, targetDate: Date?, priority: String?, category: String?, progress: Int?) {
...
// 기존 코드
saveMemoListToUserDefaults()
}
// 메모 삭제
func deleteMemo(at index: Int) {
...
// 기존 코드
saveMemoListToUserDefaults()
}
// 모든 메모 삭제 - (3)
func deleteAllCompletedMemos() {
var indexesToRemove: [Int] = []
for (index, memo) in memoList.enumerated() {
if !memo.isCompleted {
indexesToRemove.append(index)
}
}
// 인덱스를 역순으로 정렬한 후 삭제(정확한 삭제를 위해)
let reversedIndexes = indexesToRemove.sorted(by: >)
for index in reversedIndexes {
deleteMemo(at: index)
}
}
// 접근 제어 - (2)
private func saveMemoListToUserDefaults() {
if let data = try? JSONEncoder().encode(memoList) {
userDefaults.set(data, forKey: "MemoList")
}
}
}
- 기존에 있는 코드를 그대로 살리고 MemoList를 생성/수정/삭제할 때 UserDefaults로 저장하는 함수를 추가하는 형태로 구현
- (1) UserDefaults를 통해 메모 불러오기
- UserDefaults에서 MemoList라는 키로 저장된 데이터를 가져와서 JSONDecoder를 사용하여 Memo 타입의 배열로 디코딩함
- 디코딩 성공 시 디코딩된 메모 리스트를 memoList에 할당
- 이전에 저장된 메모 데이터를 복원하여 클래스 내부에서 활용할 수 있음
- (2) UserDefaults에 저장하기
- JSONEncoder를 사용하여 memoList 배열을 데이터로 변환(인코딩)
- 변환된 데이터를 MemoList라는 키로 UserDefaults에 저장
- 앱을 종료해도 저장된 데이터를 불러올 수 있음
- (3) UserDefaults를 통해 삭제 및 전체삭제 구현
- 기존에 삭제 로직을 아래와 같이 두개로 나누어 코드를 작성 : 할 일 확인하기(전체) 목록에서의 삭제, 완료한 일 목록에서의 개별 및 전체 삭제
- 전체 목록에서의 삭제는 UserDefaults를 적용해야 앱을 종료했다 켜도 삭제된게 유지됨
- 그러나 완료한 일 목록에서는 어차피 삭제하면 전체 목록에서 삭제되니까 deleteMemo 함수 안에 있는 UserDefaults가 자동으로 적용됨. 그래서 전체 삭제 로직에는 추가하지 않아도 됨
🤳🏻 적용화면
URLSession
- 기존 메모 앱 홈 화면에는 Asset을 이용하여 이미지뷰를 구성
- URLSession을 이용해서 본 포스팅 상단에 있는 이미지로 바꿔보려고 함
- 주의할 점 : UI는 Main Thread에서 업데이트 해야 함
URLManager
class URLManager {
static let shared = URLManager()
private init() {
}
let mainImageUrl = URL(string: "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgdOoN%2Fbtsr4LBjZmp%2FPgZjUykhxAOcuTxiMIlDKk%2Fimg.png")!
func getJsonData(completion: @escaping (Result<Data, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: mainImageUrl) { data, response, error in
if let error {
print("Error: Network Error")
completion(.failure(NetworkError.emptyResponse))
return
}
guard let data = data else {
completion(.failure(NetworkError.emptyResponse))
return
}
completion(.success(data))
}
task.resume()
}
}
enum NetworkError: Error {
case emptyResponse
}
- 🚨 DispatchQueue를 통해 main에서 업데이트!
- 코드설명 : 이전 포스팅에서 코드에 대한 설명을 자세하게 기재해두었으니 참조
🔗 2023.08.22 - [TIL] - [Swift] URLSession 데이터 불러오기, UserDefaults CRUD
[Swift] URLSession 데이터 불러오기, UserDefaults CRUD
URLSession - 🔗 URLSession은 네트워크 작업을 비동기적으로 처리하며, 데이터를 가져오거나 업로드하는 등의 다양한 작업을 효과적으로 수행할 수 있는 도구이다. URLSession은 네트워크 작업을 처리
ahrzosel.tistory.com
🤳🏻 적용화면
그 외 문제발견 및 해결
- 문제발견 → 디테일페이지에서 수정 후 저장 누르고 이전 화면으로 돌아가지 않음
- 지난 프로젝트 작업 당시에는 수정 후 저장할 때 확인이나 취소같은 액션을 추가하지 않음, 대신 저장하기 누르면 바로 저장하고 수정되었다는 Alert만 띄웠음
- 저장을 누르고 Alert를 띄워주고 이전 뷰컨트롤러로 이동하게 하고 싶은데, popViewController 코드를 넣을 부분이 굉장히 애매
- Alert는 title만 보이고 dismiss 되니까 이전에 넣기에는 알람이 안사라지고, 이후에 넣자니 구현이 안됨
- 문제가 뭔지 확인이 필요
- Unwind Segue는 IBAction이 동시에 적용이 안되서 불가
- 수정/취소 버튼을 만들면 구현하는데 어렵지 않으나, 나는 버튼선택하는 과정을 없애고 싶고 바로 저장도 됐으면 좋겠음…
- 기존 코드 사이에 프린트문도 넣어봤는데 이전화면으로 돌아가지는 않으면서 프린트문은 나오는 기이한 현상...
>> dismiss - popViewController 코드 - 프린트문
- (원인 확인) dismiss 되버려서 다음 코드를 실행할 수 있는 시간이 없어서 안됐던거임..
- (해결방법) dismiss 이후 popViewController를 DispatchQueue를 통해 구현
let checkAlert = UIAlertController(title: "수정되었습니다.", message: "", preferredStyle: .alert)
self.present(checkAlert, animated: false)
dismiss(animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.navigationController?.popViewController(animated: true)
}
🤳🏻 적용화면
728x90
반응형
'TIL' 카테고리의 다른 글
[Swift] 메모 앱 만들기 심화 (3) : URL Session 으로 API 통신하여 고양이 사진 가져오기 (0) | 2023.08.26 |
---|---|
[Swift] 메모 앱 만들기 심화 (2) : TableView Section/Header/Footer (3) | 2023.08.24 |
[Swift] URLSession 데이터 불러오기, UserDefaults CRUD (4) | 2023.08.22 |
[Swift] SNS 앱 만들기 (3) : 프로젝트 회고 (0) | 2023.08.21 |
[Swift] 타입캐스팅 (0) | 2023.08.19 |