# 자이둜 App - REAEME # πŸ“ˆ 자이둜 App ![iOS badge](https://img.shields.io/badge/Swift-F05138?style=flat&logo=Swift&logoColor=white) ![iOS badge](https://img.shields.io/badge/iOS-14.0%2B-blue) > πŸ‘©πŸ»β€πŸ’» 2023.01.30~ 2023.02.04 **μœ μ €μ˜ λͺ¨λ°”μΌκΈ°κΈ°μ˜ μ‹€μ‹œκ°„ 자이둜, 가속도λ₯Ό μΈ‘μ •ν•˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€.** - 0.1초 λ‹¨μœ„λ‘œ μ΅œλŒ€ 60μ΄ˆκΉŒμ§€ μΈ‘μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. - μΈ‘μ • κ°’μ˜ X, Y, Z μΆ• λ³€ν™”λ₯Ό μ‹€μ‹œκ°„μœΌλ‘œ λ³€ν•˜λŠ” κ·Έλž˜ν”„λ‘œ λ³΄μ—¬μ€λ‹ˆλ‹€. - μ €μž₯ 된 λ°μ΄ν„°λ‘œ κ·Έλž˜ν”„ μ• λ‹ˆλ©”μ΄μ…˜μ„ μž¬μƒν•˜κ±°λ‚˜ μ΅œμ’… κ²°κ³Ό κ·Έλž˜ν”„λ‘œ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. --- ## πŸ“– λͺ©μ°¨ 1. [νŒ€ μ†Œκ°œ](#-νŒ€-μ†Œκ°œ) 2. [κΈ°λŠ₯ μ†Œκ°œ](#-κΈ°λŠ₯-μ†Œκ°œ) 3. [κ°œλ°œν™˜κ²½ 및 적용기술](#-κ°œλ°œν™˜κ²½-및-적용기술) 4. [Class Diagram](#-class-diagram) 5. [폴더 ꡬ쑰](#-폴더-ꡬ쑰) 6. [νƒ€μž„λΌμΈ](#-νƒ€μž„λΌμΈ) 7. [ν”„λ‘œμ νŠΈμ—μ„œ κ²½ν—˜ν•˜κ³  배운 것](#-ν”„λ‘œμ νŠΈμ—μ„œ-κ²½ν—˜ν•˜κ³ -배운-것) 8. [κ³ λ―Όν•œ λΆ€λΆ„](#-κ³ λ―Όν•œ-λΆ€λΆ„) 9. [νŠΈλŸ¬λΈ” μŠˆνŒ…](#-νŠΈλŸ¬λΈ”-μŠˆνŒ…) 10. [μ°Έκ³  링크](#-μ°Έκ³ -링크) --- ## 🌱 νŒ€ μ†Œκ°œ <table> </tr> <tr> <td style="text-align:center" > <a href="https://github.com/sunny-maeng">μ¨λ‹ˆμΏ ν‚€</a> </td> <td style="text-align:center" > <a href= "https://github.com/yuvinrho"> 둜빈 </td> </tr> <td style="text-align:center" > <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/107384230?v=4"> </td> <td style="text-align:center" > <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/49301866?v=4"> </td> </tr> <tr> <td style="text-align:center" > - 데이터λͺ©λ‘ ν™”λ©΄ View와 Logic λ‹΄λ‹Ή <br> - λ‹€μ‹œλ³΄κΈ° ν™”λ©΄ View λ‹΄λ‹Ή <br> - CoreDataλ₯Ό ν†΅ν•œ 데이터 관리 <br> - 데이터 핸듀링 Error처리 <br> - GraphView MVVM λ””μžμΈνŒ¨ν„΄ 적용 </td> <td style="text-align:center" > - μΈ‘μ •ν™”λ©΄ View와 Logic λ‹΄λ‹Ή <br> - λ‹€μ‹œλ³΄κΈ° ν™”λ©΄ Logic λ‹΄λ‹Ή <br> - 자이둜, 가속도 μΈ‘μ • <br> - κ·Έλž˜ν”„ 그리기 <br> - FileManagerλ₯Ό ν†΅ν•œ 데이터 관리 </td> </tr> <tr> <td colspan= "2"> <strong><center>곡톡기여</center></strong> </td> </tr> <tr> <td colspan= "2" style="text-align:center" > - Modelνƒ€μž… ν˜‘μ˜ κ΅¬ν˜„ <br> - μΈ‘μ •ν•œ 데이터 λΉ„λ™κΈ°λ‘œ μ €μž₯ </td> </table> --- ## πŸ“± κΈ°λŠ₯ μ†Œκ°œ ### 1. Main ν™”λ©΄ - 자이둜, 가속도 츑정데이터 리슀트 ν™”λ©΄μž…λ‹ˆλ‹€. - μ €μž₯된 데이터λ₯Ό 10κ°œμ”© λΆˆλŸ¬μ™€μ„œ λ³΄μ—¬μ€λ‹ˆλ‹€. |메인 ν™”λ©΄|νŽ˜μ΄μ§• κ΅¬ν˜„| |:-:|:-:| |<img src="https://i.imgur.com/U3LVbkv.png" width="200" height="400"/>|<img src="https://i.imgur.com/3Espvcq.gif" width="200" height="400"/>| ### 2. 가속도, 자이둜 μΈ‘μ •ν™”λ©΄ - μ„Έκ·Έλ¨ΌνŠΈ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ Accelerometer, Gyro μ„Όμ„œλ₯Ό μ„ νƒν•œ ν›„ μΈ‘μ •λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 0.1초 λ‹¨μœ„λ‘œ μΈ‘μ •ν•œ 데이터λ₯Ό κ·Έλž˜ν”„μ— λ³΄μ—¬μ€λ‹ˆλ‹€. - μ΅œλŒ€ 1λΆ„ λ™μ•ˆ 데이터λ₯Ό μΈ‘μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. - μ €μž₯λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ μΈ‘μ •ν•œ κ²°κ³ΌλŠ” CoreData, FileManagerλ₯Ό μ΄μš©ν•΄ λ””λ°”μ΄μŠ€μ— μ €μž₯λ©λ‹ˆλ‹€. - μ €μž₯μ‹œ Activity Indicatorλ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€. - μ €μž₯μ‹€νŒ¨μ‹œ μ•ŒλžŒμ„ ν‘œμ‹œν•©λ‹ˆλ‹€. |가속도 μΈ‘μ •|자이둜 μΈ‘μ •| |:-:|:-:| |<img src="https://i.imgur.com/Cgq7hAh.gif" width="200" height="400"/>|<img src="https://i.imgur.com/j7hV3HY.gif" width="200" height="400"/>| |μ €μž₯ 성곡|μ €μž₯ μ‹€νŒ¨| |:-:|:-:| |<img src="https://i.imgur.com/uzVadZu.gif" width="200" height="400"/>|<img src="https://i.imgur.com/jggWbFZ.gif" width="200" height="400"/>| ### 3. μ €μž₯ν•œ 자이둜, 가속도 μΈ‘μ •κ²°κ³Ό κ·Έλž˜ν”„ν™”λ©΄ - μ €μž₯ν•œ 데이터λ₯Ό κ·Έλž˜ν”„λ‘œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. - 메인 ν™”λ©΄μ—μ„œ 셀을 ν„°μΉ˜ν•˜λ©΄ κ·Έλž˜ν”„λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. - 셀을 μŠ€μ™€μ΄ν”„ν•˜μ—¬ Playλ²„νŠΌμ„ λˆ„λ₯΄λ©΄ κ·Έλž˜ν”„λ₯Ό λ¦¬ν”Œλ ˆμ΄ν•΄μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. |κ·Έλž˜ν”„|κ·Έλž˜ν”„ λ¦¬ν”Œλ ˆμ΄| |:-:|:-:| |<img src="https://i.imgur.com/raGTBZm.gif" width="200" height="400"/>|<img src="https://i.imgur.com/JlbLfgt.gif" width="200" height="400"/>| ### 4. 닀크λͺ¨λ“œ 지원 - μ‚¬μš©μžλ₯Ό μœ„ν•΄ 닀크λͺ¨λ“œλ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. |메인화면|μΈ‘μ •ν•˜κΈ° ν™”λ©΄|κ·Έλž˜ν”„ ν™”λ©΄| |:-:|:-:|:-:| |<img src="https://i.imgur.com/xY5fwO5.png" width="200" height="400"/>|<img src="https://i.imgur.com/zqgWR1P.png" width="200" height="400"/>|<img src="https://i.imgur.com/i22rieg.gif" width="200" height="400"/>| --- ## πŸ›  κ°œλ°œν™˜κ²½ 및 적용기술 ![iOS badge](https://img.shields.io/badge/Swift-V5.7-red) ![iOS badge](https://img.shields.io/badge/Xcode-V14.2-blue) | UI | 데이터 μ €μž₯ | μ•„ν‚€ν…μ²˜ | | :--------: | :--------: | :--------: | | <img height = 90, src = "https://i.imgur.com/q6rTXrE.png"> | <img height = 90, src = "https://i.imgur.com/DSnI74h.png"> <img height = 90, src = "https://i.imgur.com/p6nJlhN.png"> | <img height = 70, src = "https://i.imgur.com/FWud4LR.png"> <img height = 70, src = "https://i.imgur.com/TY8lr5s.png"> | UIKit <br> (Only Code) | CoreData / FileManager | MVC + λΆ€λΆ„ MVVM | - **MVC 채택 이유** - MVCνŒ¨ν„΄μ€ κ°€μž₯ λΉ λ₯΄κ²Œ κ΅¬ν˜„ν•  수 μžˆλŠ” λͺ¨λΈλ‘œ Model, View, Controller 관심사λ₯Ό λΆ„λ¦¬ν•˜μ—¬ μœ μ§€λ³΄μˆ˜λ₯Ό μ‰½κ²Œ ν•  수 μžˆλ‹€κ³  생각해 μ±„νƒν–ˆμŠ΅λ‹ˆλ‹€. - MVCνŒ¨ν„΄μ€ ν”νžˆ λ§ν•˜λŠ” `Massiveν•œ ViewController`κ°€ 될 수 μžˆλŠ” 단점이 μžˆμ§€λ§Œ ν˜„μž¬ ν”„λ‘œμ νŠΈλŠ” ν•œ 개의 ν™”λ©΄μ—μ„œ λ‹΄λ‹Ήν•˜λŠ” 둜직이 λ§Žμ§€ μ•Šλ‹€κ³  νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€. - **MVVM 적용 이유** - κ·Έλž˜ν”„λ₯Ό κ·Έλ¦¬λŠ” GraphicViewλŠ” `BezierPath`λ₯Ό μ΄μš©ν•΄ μ‹€μ‹œκ°„μœΌλ‘œ λ·°λ₯Ό μ—†λ°μ΄νŠΈ ν•΄μ•Όν•˜λŠ”λ°, `Model`을 직접 κ°€μ§€κ³  κ·Έλž˜ν”„λ₯Ό κ·Έλ¦¬λŠ”κ²Œ μš©μ΄ν•˜λ‹€ νŒλ‹¨ν•΄ `ViewModel`을 μ μš©ν–ˆμŠ΅λ‹ˆλ‹€. --- ## πŸ‘€ Class Diagram ### πŸ‘‰ MVC + λΆ€λΆ„ MVVM |![](https://i.imgur.com/IM5jq8B.jpg)| |---| ### πŸ‘‰ DataStorage 계측ꡬ쑰 |<img width = 500, src ="https://i.imgur.com/Dm9Q1HU.png">| |---| --- ## πŸ—‚ 폴더 ꡬ쑰 ``` GyroData β”œβ”€β”€ Views β”‚ β”œβ”€β”€ LineGraphView β”‚ β”œβ”€β”€ ListCell β”‚ β”œβ”€β”€ ListView β”‚ β”œβ”€β”€ MeasurementView β”‚ └── ReviewView β”‚ β”œβ”€β”€ Controllers β”‚Β Β  β”œβ”€β”€ ListViewController β”‚Β Β  β”œβ”€β”€ MeasurementViewController β”‚Β Β  └── ReviewViewController β”‚ β”œβ”€β”€ Models β”‚Β Β  β”œβ”€β”€ AxisValue β”‚Β Β  β”œβ”€β”€ Measurement β”‚Β Β  β”œβ”€β”€ Sensor β”‚Β Β  └── SensorManager β”‚ β”œβ”€β”€ Extension + β”‚Β Β  β”œβ”€β”€ Date + β”‚Β Β  β”œβ”€β”€ JSONDecoder + β”‚Β Β  β”œβ”€β”€ JSONEncoder + β”‚Β Β  β”œβ”€β”€ UIAlertController + β”‚Β Β  β”œβ”€β”€ UILabel + β”‚Β Β  └── UIStackView + β”‚ β”œβ”€β”€ DataStorage β”‚Β Β  β”œβ”€β”€ DataHandleableProtocol β”‚Β Β  β”œβ”€β”€ DataHandleError β”‚Β Β  β”œβ”€β”€ CoreData β”‚Β Β  β”‚Β Β  β”œβ”€β”€ CoreDataManager+DataHandleable β”‚Β Β  β”‚Β Β  └── MeasurementCoreModel β”‚Β Β  └── FileManager β”‚Β Β  └── SensorFileManager β”‚ └── Supporting Files Β  β”œβ”€β”€ AppDelegate Β  β”œβ”€β”€ SceneDelegate Β  β”œβ”€β”€ Base.lproj Β  β”‚Β Β  └── LaunchScreen.storyboard Β  β”œβ”€β”€ Info.plist Β  └── Assets.xcassets ``` --- ## ⏰ νƒ€μž„λΌμΈ ### πŸ•› Step1 - (총 2일) 2023.01.30 ~ 2023.01.31 | | μ§„ν–‰ λ‚΄μš© | | :--------: | -------- | | 1 | Model ν˜‘μ˜ 및 생성 | | 2 | `ListView` 그리기 및 `ListViewController` 둜직 κ΅¬ν˜„ | | 3 | `MeasurementView` 그리기 및 `MeasurementViewController` 둜직 κ΅¬ν˜„ | ### πŸ•’ Step2 - (총 4일) 2023.02.01 ~ 2023.02.04 | | μ§„ν–‰ λ‚΄μš© | | :--------: | -------- | | 1 | 데이터 처리 νƒ€μž… κ΅¬ν˜„ 및 적용 - `CoreDataManager`, `SensorFileManager` 생성 | | 2 | `ReviewPageView` 그리기 및 `ReviewPageViewController` 둜직 κ΅¬ν˜„ | | 3 | 데이터 처리 쀑 Error λ°œμƒ Alert 처리 | --- ## πŸ“ ν”„λ‘œμ νŠΈμ—μ„œ κ²½ν—˜ν•˜κ³  배운 것 - [X] CoreDataλ₯Ό μ΄μš©ν•œ TableView Paging - `scrollView Delegate`와 `offset`을 μ‚¬μš©ν•΄ ν…Œμ΄λΈ”λ·° λ§ˆμ§€λ§‰ 슀크둀 지점을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. - CoreData의 `fetchLimit`, `fetchOffset`λ₯Ό μ‚¬μš©ν•΄ 10κ°œμ”© 데이터λ₯Ό κ°€μ Έ 올 수 μžˆμŠ΅λ‹ˆλ‹€. - [X] CoreMotion을 μ΄μš©ν•œ μ„Όμ„œ(자이둜, 가속도)μΈ‘μ • - `CoreMotion`κ³Ό `Timer`λ₯Ό μ΄μš©ν•˜μ—¬ μ„€μ •ν•œ interverλ§ˆλ‹€ 데이터λ₯Ό μΈ‘μ •ν•˜κ³  νƒ€μž„μ•„μ›ƒμ΄ 되면 츑정을 μ •μ§€ν•©λ‹ˆλ‹€. - [X] `FileManager`λ₯Ό μ΄μš©ν•΄ μΈ‘μ •ν•œ 데이터 λ””λ°”μ΄μŠ€μ— μ €μž₯ - μΈ‘μ •ν™”λ©΄μ—μ„œ μ €μž₯ λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ λ””λ°”μ΄μŠ€μ— json파일둜 μ €μž₯ν•©λ‹ˆλ‹€. --- ## πŸ’­ κ³ λ―Όν•œ λΆ€λΆ„ ### 1️⃣ 좔후에 데이터양이 λ§Žμ•„μ Έ λ°μ΄ν„°μ²˜λ¦¬ κΈ°μˆ μŠ€νƒμ΄ 변경될 λ•Œ μˆ˜μ •μ˜ μš©μ΄ν•¨ κ³ λ € ν˜„μž¬λŠ” 데이터 처리λ₯Ό `CoreData`와 `FimeManager`λ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 좔후에 데이터양이 λ§Žμ•„μ Έ μ„œλ²„μ— μ €μž₯ν•΄μ•Όν•œλ‹€λ˜κ°€, λ°μ΄ν„°λ² μ΄μŠ€ κΈ°μˆ μŠ€νƒμ΄ 변경될 λ•Œ μˆ˜μ •μ΄ μš©μ΄ν•˜λ„λ‘ ν•˜κ³  μ‹Άμ—ˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ `DataHandleable`둜 ν”„λ‘œν† μ½œ μΆ”μƒν™”ν–ˆμŠ΅λ‹ˆλ‹€. 이 ν”„λ‘œν† μ½œμ€ `DataType`을 `associatedtype`으둜 μ§€μ •ν•΄μ„œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그리고 데이터 μ²˜λ¦¬μ— ν•„μš”ν•œ CRUD 쀑 ν˜„μž¬ ν”„λ‘œμ νŠΈμ—μ„œ ν•„μš”ν•œ 데이터 μ €μž₯(Create), 데이터 κ°€μ Έμ˜€κΈ°(Read), 데이터 μ‚­μ œ(Delete) λ©”μ„œλ“œλ₯Ό ν•„μˆ˜κ΅¬ν˜„ λ©”μ„œλ“œλ‘œ μ„ μ–Έλ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€. 저희 ν”„λ‘œμ νŠΈμ—μ„œλŠ” `Measurement`ꡬ쑰체(DTO)λ₯Ό μ΄μš©ν•΄ 데이터λ₯Ό 닀루고 고있기 λ•Œλ¬Έμ— ν”„λ‘œν† μ½œ 상속을 μ΄μš©ν•΄ `DataType`을 `Measurement`둜 μ‚¬μš©ν•˜λŠ” `MeasurementDataHandleable` ν”„λ‘œν† μ½œμ„ μƒμ„±ν–ˆκ³ , `CoreData`와 `FimeManager`λ₯Ό κ΄€λ¦¬ν•˜λŠ” `Class`μ—μ„œ `MeasurementDataHandleable`ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. 둜직 쀑, CRUD λ©”μ„œλ“œλ‘œ 데이터λ₯Ό μ²˜λ¦¬ν•΄μ•Όν•  λ•Œ, μΆ”μƒν™”νƒ€μž…μΈ`MeasurementDataHandleable`의 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ„λ‘ κ΅¬ν˜„ν•΄ λ†“μ•„μ„œ μΆ”ν›„ λ°μ΄ν„°μ²˜λ¦¬ κΈ°μˆ μŠ€νƒμ΄ λ³€κ²½λ˜λ”λΌλ„ 이 ν”„λ‘œν† μ½œμ„ 채택해 CRUDλ‘œμ§μ„ κ΅¬ν˜„ν•˜κ³ , μΈμŠ€ν„΄μŠ€λ§Œ κ°ˆμ•„ λΌμ›Œμ€€ ν›„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ### 2️⃣ μΈ‘μ •ν•œ 데이터 μ €μž₯μ‹œ Activity Indicator ν‘œμ‹œ μΈ‘μ •ν™”λ©΄μ—μ„œ μ €μž₯λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ `Activity Indicator`κ°€ 화면에 λ‚˜μ˜€λ©΄μ„œ λΉ„λ™κΈ°λ‘œ μ €μž₯되게 κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 이 κ³Όμ •μ—μ„œ `Activity Indicator`κ°€ 보이지 μ•Šκ³  λ°”λ‘œ μ €μž₯λ˜λŠ” λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή λ¬Έμ œλŠ” μ•„λž˜μ™€ 같이 @escaping closureλ₯Ό μ΄μš©ν•˜μ—¬ `storeDataInDevice` λ©”μ„œλ“œκ°€ λλ‚˜λ©΄ `Activity Indicator`κ°€ λ©ˆμΆ”λ„λ‘ ν•˜μ—¬ ν•΄κ²°ν•˜μ˜€μŠ΅λ‹ˆλ‹€. ```swift startActivityIndicator() storeDataInDevice { self.stopActivityIndicator() DispatchQueue.main.async { self.navigationController?.popViewController(animated: false) } ``` ### 3️⃣ Errorκ°€ λ°œμƒν•˜λ©΄ Userκ°€ μ•±μ˜ μƒνƒœλ₯Ό μ•Œ 수 μžˆλ„λ‘ Alert 처리 μ €μž₯된 츑정값듀을 λΆˆλŸ¬μ˜€κ±°λ‚˜ μΈ‘μ •ν•œ 값을 μ €μž₯ν•˜κ³  μ‚­μ œν•  λ•Œ λ°œμƒν•  수 μžˆλŠ” `Error`듀을 `DataHandleError` μ—΄κ±°ν˜•μœΌλ‘œ μ •λ¦¬ν•˜κ³ , Error의 `localizedDescription`을 `overriding` ν–ˆμŠ΅λ‹ˆλ‹€. Errorκ°€ λ°œμƒν•˜λ©΄ "Error" `Alert`을 띄어주고, `overriding`ν•œ localizedDescription을 Alert의 `Message`둜 μ‚¬μš©ν•΄ μœ μ €κ°€ μ•±μ˜ 상황을 μ•Œ 수 μžˆλ„λ‘ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€. | ![](https://i.imgur.com/N8y9PGZ.png) | ![](https://i.imgur.com/j3RIuJv.png) | ![](https://i.imgur.com/zGQ8hph.png) | | -------- | -------- | -------- | --- ## πŸ”— μ°Έκ³  링크 [κ³΅μ‹λ¬Έμ„œ] - [Developer - Article: Getting Raw Gyroscope Events](https://developer.apple.com/documentation/coremotion/getting_raw_gyroscope_events) - [Developer - Core Motion](https://developer.apple.com/documentation/coremotion) - [Developer - Core Data](https://developer.apple.com/documentation/coredata) - [Developer - FileManager](https://developer.apple.com/documentation/foundation/filemanager) - [Developer - Article: About Apple File System](https://developer.apple.com/documentation/foundation/file_system/about_apple_file_system)