{"id":18696074,"url":"https://github.com/jacobkosmart/todolist-ios-practice","last_synced_at":"2025-07-18T05:37:16.429Z","repository":{"id":140609238,"uuid":"432867848","full_name":"jacobkosmart/todoList-ios-practice","owner":"jacobkosmart","description":"To practice UITableView, UIAlertContoller, UserDefaults in storyboard","archived":false,"fork":false,"pushed_at":"2021-12-01T11:55:22.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-19T00:37:31.555Z","etag":null,"topics":["storyboard","swift","uialertcontroller","uitableview","userdefaults"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacobkosmart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-29T01:20:44.000Z","updated_at":"2024-05-21T06:12:17.000Z","dependencies_parsed_at":"2023-03-27T10:56:43.972Z","dependency_job_id":null,"html_url":"https://github.com/jacobkosmart/todoList-ios-practice","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jacobkosmart/todoList-ios-practice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FtodoList-ios-practice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FtodoList-ios-practice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FtodoList-ios-practice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FtodoList-ios-practice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacobkosmart","download_url":"https://codeload.github.com/jacobkosmart/todoList-ios-practice/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FtodoList-ios-practice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265705617,"owners_count":23814488,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["storyboard","swift","uialertcontroller","uitableview","userdefaults"],"created_at":"2024-11-07T11:17:02.936Z","updated_at":"2025-07-18T05:37:16.378Z","avatar_url":"https://github.com/jacobkosmart.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TodoList-ios-practice\n\n\u003cimg width=\"350\" alt=\"스크린샷\" src=\"https://user-images.githubusercontent.com/28912774/144228449-51aee162-e618-4849-bc02-7a63928bafb5.gif\"\u003e\n\n## 기능 상세\n\n- TableView 에 할일을 추가할 수 있습니다\n\n- TableView 에서 할일을 삭제 할 수 있습니다\n\n- TableView 에서 할일을 재정렬할 수 있습니다\n\n- 할일들을 데이터 저장소에 저장을 하여 앱을 재실행 하여도 데이터가 유지 되게 합니다\n\n## Check Point !\n\n### UITableView\n\n- 할일들이 표시되게 하기\n\n```swift\nextension ViewController: UITableViewDataSource {\n\t// 행의 갯수를 묻는 method : tasks 의 갯수\n\tfunc tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n\t\treturn self.tasks.count\n\t}\n\t// 특정 row 를 그리기위해 특정 cell 을 반환하는 method: todo 목록이 표시되게 작성\n\t// dequeueReusableCell : 재사용 가능한 cell 을 반환하여, 이를 table view 에 추가 하는 역활 queue 를 사용해서 cell 을 재사용 (즉, 메모리 누수를 방지하기 위해서 만약 view 에서 5개의 list 가 보이게 되면 5개의 cell 만 메모리를 사용하고 스크롤을 내렸을때, 안보이는 전의 cell 을 다시 재사용해서 재할당하는것임)\n\tfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n\t\tlet cell = tableView.dequeueReusableCell(withIdentifier: \"Cell\", for: indexPath)\n\t\t// 0부터 tasks의 count 갯수 까지의 task list\n\t\tlet task = self.tasks[indexPath.row]\n\t\tcell.textLabel?.text = task.title\n\t\tif task.done {\n\t\t\t// task 의 done 값이 true 이면 checkmark 가 나타나게 함\n\t\t\tcell.accessoryType = .checkmark\n\t\t} else {\n\t\t\tcell.accessoryType = .none\n\t\t}\n\t\treturn cell\n\t}\n\n\t//  commit forRowAt 는 삭제버튼을 누를때 그 누른 cell 이 어떤 버튼인지 알려주는 기능\n\tfunc tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {\n\t\tself.tasks.remove(at: indexPath.row)\n\t\ttableView.deleteRows(at: [indexPath], with: .automatic)\n\n\t\t// 모든 셀이 삭제되면 편집모드로 빠져나오게 함\n\t\tif self.tasks.isEmpty {\n\t\t\tself.doneBtnTap()\n\t\t}\n\t}\n\n\t// 재정렬 할 수 있게 움직이게 하는 canMoveAt\n\tfunc tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -\u003e Bool {\n\t\treturn true\n\t}\n\n\t// 편집모드에서 할일의 순서를 변경하는 기능 : moveRowAt: 행이 다른 위치로 이동하면, sourceIndexPath 의 위치를 통해 원래 있었던 위치를 알려 주고, destinationIndexPath 를 통해 어디로 이동했는지 알려주는 logic\n\tfunc tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {\n\t\t// 재정렬 된것을 다시 tasks 에 저장해줘야 함\n\t\tvar tasks = self.tasks\n\t\tlet task = tasks[sourceIndexPath.row]\n\t\t// 원래 위치에 있던 할일을 삭제하고\n\t\ttasks.remove(at: sourceIndexPath.row)\n\t\t// 이동한 위치에 값을 넣어주고\n\t\ttasks.insert(task, at: destinationIndexPath.row)\n\t\t// 재정렬된 tasks 를 원래 tasks 의 값에 넣어줌\n\t\tself.tasks = tasks\n\t}\n}\n\n\n// check 표시를 하기위한 delegate extension\nextension ViewController: UITableViewDelegate {\n\t// cell 이 선택되었을때 어떠한 cell 이 선택 되었는지 알려주는 method\n\tfunc tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n\t\tvar task = self.tasks[indexPath.row]\n\t\ttask.done = !task.done // done 의 값이 초기값은 false 인데 false 이면 true 로, true 이면 false 로 변환\n\t\t// 변경된 done 을 원래 tasks 에 덮어 씌우기\n\t\tself.tasks[indexPath.row] = task\n\t\t// 선택된 cell 만 reload 하기 at 은 선택된 index 위치를 indexPath , with 은 animation 속성인데 .automatic 하면 system의 속성을 그대로 사용한다는 의미임\n\t\tself.tableView.reloadRows(at: [indexPath], with: .automatic)\n\t}\n}\n\n```\n\n### UIAlertController\n\n- 앱의 alert 을 표시하기\n\n```swift\n@IBAction func tabAddButton(_ sender: UIBarButtonItem) {\n\t\t// add 버튼을 누르면 alert 이 생성되는 btn 생성\n\t\tlet alert = UIAlertController(title: \"Create Todo\", message: nil, preferredStyle: .alert)\n\n\t\t// Add 버튼 생성 - 등록 버튼을 누를때 textField 의 버튼을 가져오기\n\t\tlet registerBtn = UIAlertAction(title: \"ADD\", style: .default, handler: { [weak self] _ in\n\t\t\tguard let title = alert.textFields?[0].text else { return }\n\t\t\tlet task = Task(title: title, done: false)\n\t\t\tself?.tasks.append(task)\n\t\t\t// task 가 추가 될때 마다 tableView 를 reload 하기\n\t\t\tself?.tableView.reloadData()\n\t\t})\n\n\t\t// cancel 되는 btn 생성\n\t\tlet cancelBtn = UIAlertAction(title: \"CANCEL\", style: .cancel, handler: nil)\n\t\t// add, cancel btn 을 alert 에 추가하기\n\t\talert.addAction(cancelBtn)\n\t\talert.addAction(registerBtn)\n\t\t// textField 생성하기\n\t\talert.addTextField(configurationHandler: { textField in\n\t\t\ttextField.placeholder = \"Write your task.\"\n\t\t})\n\t\t// 화면에 present 하기\n\t\tself.present(alert, animated: true, completion: nil)\n\t}\n```\n\n### UserDefaults\n\n- 앱의 데이터가 local 에 저장하게 하기\n\n```swift\n\n\t// useDefault method 생성\n\tfunc saveTasks(){\n\t\t// 배열에 있는 tasks 를 dic 형태로 mapping 하기\n\t\tlet data = self.tasks.map {\n\t\t\t[\n\t\t\t\t\"title\": $0.title,\n\t\t\t\t\"done\": $0.done\n\t\t\t]\n\t\t}\n\t\tlet userDefaults = UserDefaults.standard\n\t\t// userDefaults 의 data 를 넣고, Key 값으로는 tasks 로 설정함\n\t\tuserDefaults.set(data, forKey: \"tasks\")\n\t}\n\n\t// 저장된 data load 하는 method\n\tfunc loadTasks() {\n\t\tlet userDefaults = UserDefaults.standard\n\t\tguard let data = userDefaults.object(forKey: \"tasks\") as? [[String: Any]] else { return }\n\t\t// 불러온 data 를 tasks 에 다시 저장\n\t\tself.tasks = data.compactMap {\n\t\t\tguard let title = $0[\"title\"] as? String else { return nil }\n\t\t\tguard let done = $0[\"done\"] as? Bool else { return nil }\n\t\t\treturn Task(title: title, done: done)\n\t\t}\n\t}\n}\n\n```\n\n## reference\n\nJacob's DevLog - [https://jacobko.info/ios/ios-04/](https://jacobko.info/ios/ios-04/)\n\n상어의 개발 블로그 - [https://shark-sea.kr/entry/iOS-TableView-Storyboard%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0](https://shark-sea.kr/entry/iOS-TableView-Storyboard%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0)\n\n평생 공부만해야할듯 - [https://gigas-blog.tistory.com/49](https://gigas-blog.tistory.com/49)\n\nfastcampus - [https://fastcampus.co.kr/dev_online_iosappfinal](https://fastcampus.co.kr/dev_online_iosappfinal)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobkosmart%2Ftodolist-ios-practice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacobkosmart%2Ftodolist-ios-practice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobkosmart%2Ftodolist-ios-practice/lists"}