map based app
Apple Store
https://apps.apple.com/kr/app/lamp-๋จํ-์ฌํ-์ง๋/id1641478631
GitHub
https://github.com/umaKim/LampUIkit
- 1 iOS Developer - Kim Yoonsuk (Uma Kim)
- 1 Backend Developer
- 1 Designer
- 1 Product Manager (Presenter)
- Swift, iOS
- Combine
- AutoLayout
- FloatingPanel
- SDWebImage
- GoogleMap SDK
- SkeletonView
- Lottie
- Alamofire
- CombineCocoa
- UmaBasicAlertKit
- Kakao SDK
- Quick, Nimble
- Implemented asynchronous processing to display map pins with animation in the order that the photos are loaded
- Utilized Combine framework for implementation
- Reduced code duplication and increased code reusability through modularization
- Implemented all UI and functionalities programmatically without using Storyboard
- Implemented animation for changes in cells using DiffableDataSource introduced in WWDC 19
- Used SDWebImage library for image caching to prevent performance degradation during image loading
- Implemented auto cell sizing based on content length for variable cell height and improved user experience
- Implemented localization support for English and Japanese languages
- Implemented language switching functionality within the app
- Wrote unit tests (in early stages, not fully completed)
-
Problem:
When fetching and displaying approximately 30 locations on the map, the entire process takes about 10 seconds. During this time, the user is forced to view a loading screen, resulting in a poor user experience.
-
Problem Analysis:
To understand what happens during the loading screen, I listed the sequence of events:
- Fetch an array containing information and image links of 30 locations.
- Pass the information and image links of each element in the array to a map pin object.
- The object fetches the image using the provided image link, and once all images are fetched, it proceeds to the next location and performs the same operation.
- After completing this process for all 30 locations, the loading screen is removed, and the map with map pins is displayed to the user.
The step that takes the most time is step 3, where images are fetched using URLs. This process is time-consuming because it waits for each image to be fully fetched before proceeding to the next one, blocking the main thread.
-
Solution:
Instead of waiting for all image data to be fetched, I implemented a solution where each image is displayed as soon as it is fetched. This gives the impression to the user that the map is being updated without any loading time.
: Execute the try? Data(contentsOf: url) part asynchronously on a background thread, and once the image data is fetched, update the view on the main thread.
-
Problem:
Following the existing approach, to implement fetching data from a new API, I had to repeatedly write similar code that was already written for other API calls.
-
Desired Approach:
Enable fetching of desired APIs by simply adding the endpoint, without having to write repetitive code.
-
Solution:
By using generics, I minimized the duplication of code. I actively utilized enums so that by simply adding the endpoint, the desired API could be used immediately.
- iOS ๊ฐ๋ฐ์ 1๋ช โ ๊น์ค์
- ์๋ฒ ๊ฐ๋ฐ์ 1๋ช
- ๋์์ด๋ 1๋ช
- ๊ธฐํ์(๋ฐํ์) 1๋ช
- Swift, iOS
- Combine
- AutoLayout
- FloatingPanel
- SDWebImage
- GoogleMap SDK
- SkeletonView
- Lottie
- Alamofire
- CombineCocoa
- UmaBasicAlertKit
- Kakao SDK
- Quick, Nimble
- ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํตํด์ map pin๋ค์ด ์ฌ์ง์ด ๋ก๋ฉ ๋๋ ์์๋ก ์ ๋๋ฉ์ด์ ๊ณผ ํจ๊ป ํ๋ฉด์ ๋ณด์ด๊ฒ ํจ
- Combine์ ์ฌ์ฉํด์ ๊ตฌํ
- ๋ชจ๋ํ๋ฅผ ํตํด์ ์ฝ๋์ ์์ ์ค์ด๊ณ ์ฝ๋ ์ฌํ์ฉ์ฑ์ ๋์
- ๋ชจ๋ UI์ ๋์์ Storyboard์์ด ์ฝ๋๋ก๋ง ๊ตฌํ
- WWDC 19์ ์๊ฐ๋๋ DiffableDatasource๋ฅผ ์ ์ฉํด์ Cell์ ์๊ธฐ๋ ๋ณํ์ ๋ํ ์ ๋๋ฉ์ด์ ๊ตฌํ
- SD web image ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด์ image caching์ ๊ฐ๋ฅํ๊ฒ ํด์ ์ด๋ฏธ์ง ๋ก๋ฉ์ ๋ฐ์ํ ์ ์๋ ์ฑ์ ํผํฌ๋จผ์ค ์ ํ๋ฅผ ๋ฐฉ์ง
- ์ฌ์ฉ์๊ฐ ์์ฑํ ๊ธ์ ์์ ๋ฐ๋ผ ์ ์ ํฌ๊ธฐ๊ฐ ๋ณํ ์ ์๊ฒ auto cell sizing์ ์ ์ฉํด์ ux๋ฅผ ํฅ์
- ์์ด, ์ผ๋ณธ์ด ์ง์ Localization ๊ตฌํ
- ์ฑ ๋ด๋ถ์์ ์ธ์ด ๋ฐ๊พธ๊ธฐ ๊ธฐ๋ฅ ๊ตฌํ
- Unit test ์์ฑ ( ์์ ํ ๋๋ด์ง๋ ์์ - ์์ง ์ด๋ฐ ๋จ๊ณ )
-
๋ฌธ์ :
์ง๋์์ ๋ฑ์ฅํ๋ ์ฝ 30๊ฐ์ ์ฅ์๋ฅผ fetchํ๊ณ map pin์ผ๋ก ๋์์ฃผ๋ ๋ชจ๋ ๋์์ด ์๋ฃํ ๋๊น์ง ๋ก๋ฉ ํ๋ฉด์ ๋์์ค๋ค. ๊ทธ๋ฐ๋ฐ ์ด ๋ชจ๋ ๊ณผ์ ์ด ์๋ฃ๋๋ ๋ฐ์๋ ์ฝ 10์ด ์ ๋ ์์๋๋ค. ์ฝ 10์ด ๋์ ์ฌ์ฉ์๋ ๋ก๋ฉ ํ๋ฉด๋ง์ ๋ณด๊ณ ์์ด์ผ ํ๋ค. ์ด๊ฒ์ ์ฌ์ฉ์์๊ฒ ๋ถ์พํ ๊ฒฝํ์ ์ค๋ค.
-
๋ฌธ์ ํ์ :
๋ฌธ์ ํ์ ์ ์ํด ๋ก๋ฉํ๋ฉด์ด ๋์์ ธ ์๋ ๋์ ๋ฌด์จ์ผ์ด ์ผ์ด๋๋์ง ๋์ดํด ๋ณด์๋ค.
- ์ฅ์์ ์ ๋ณด์ ์ด๋ฏธ์ง ๋งํฌ๊ฐ 30๊ฐ ๋ด๊ฒจ์๋ ๋ฐฐ์ด์ ๋ถ๋ฌ์จ๋ค. โ 2. ํด๋น ๋ฐฐ์ด์ ์๋ ๊ฐ ์์์ ์๋ ์ ๋ณด๋ค๊ณผ ์ด๋ฏธ์ง ๋งํฌ๋ฅผ map pin ๊ฐ์ฒด์ ๋๊ธด๋ค. โ 3. ๊ฐ์ฒด๋ ๋ฐ์ ์ด๋ฏธ์ง ๋งํฌ๋ฅผ ํตํด ์ด๋ฏธ์ง๋ฅผ ๋ฐ์์ค๊ณ ๋ค ๋ฐ์ ์์ผ๋ฉด ๋ค์ ์ฅ์์ ๋ํ ๋ฐ์ดํฐ๋ค์ ๊ฐ์ง๊ณ ๋ค์ ๊ฐ์ฒด์์ ๊ฐ์ ์์ ์ ํด์ค๋ค. โ 4. 3๋ฒ ์์ ์ 30๋ฒ์ ๋ค ์๋ฃํ๊ณ ๋๋ฉด ๋ก๋ฉ ํ๋ฉด์ ์ ๊ฑฐํ๊ณ ์ฌ์ฉ์์๊ฒ map pin๋ค์ด ์ฌ๋ ค์ ธ ์๋ ์ง๋๋ฅผ ๋ณด์ฌ์ค๋ค.
- ์ฌ๊ธฐ์์ ๊ฐ์ฅ ๊ธด ์๊ฐ์ด ์์๋๋ ๊ฒ์ 3๋ฒ ๊ณผ์ ์ด๋ค. ๋ค๋ฅธ ๊ณผ์ ๊ณผ๋ ๋ค๋ฅด๊ฒ URL์ ํตํด ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค๋ ๊ฒ์ ํ์ ํ๋ค. ์ด ๊ณผ์ ์์ ์ค๋ ๊ฑธ๋ฆฌ๋ ์ด์ ๋ main thread์์ ํ ๊ฐ์ฒด์ ์ด๋ฏธ์ง๊ฐ ๋ค ๋ถ๋ฌ์ฌ ๋๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ๋ค์ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋ ์์ด์ด์ ๊ทธ๋ ๋ค.
-
ํด๊ฒฐ ๋ฐฉ๋ฒ:
๋ชจ๋ image data๊ฐ ๋ฐ์ ์ฌ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ, ํ ์ด๋ฏธ์ง๊ฐ ๋ฐ์ ์์ก์ผ๋ฉด ํด๋น ์ด๋ฏธ์ง๋ถํฐ ๋จผ์ ํ๋ฉด์ ๋์์ฃผ๋ ๋ฐฉ์์ผ๋ก ๊ณ ์ณ์ผ ํ๋ค. ์ด๋ฐ ์์ผ๋ก ๊ณ ์น๋ค๋ฉด ์ฌ์ฉ์ ์ ์ฅ์์๋ ๋ก๋ฉ ์๊ฐ์ด ์์ด ์ง๋๊ฐ ์ ๋ฐ์ดํธ๋๋ ๊ฒ ์ฒ๋ผ ๋ณด์ด๊ฒ ๋๋ค.
<๊ธฐ์ ์ ํด๊ฒฐ>: try? Data(contentsOf: url) ์ด ๋ถ๋ถ์ Background Thread์์ ๋น๋๊ธฐ๋ก ์คํ์์ผ์ฃผ๊ณ image data๊ฐ ๋ฐ์์์ง๋ฉด ๊ทธ๋ main thread์์ view๋ก ์ ๋ก๋ํด์ค๋ค.
-
๋ฌธ์ :
๊ธฐ์กด์ ์๋ํ๋ ๋ฐฉ์์ ๊ณ ์งํ์๋ฉด, ์๋ก์ด API๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ์ ๊ตฌํํ๊ธฐ ์ํด์ ๊ธฐ์กด์ ๋ค๋ฅธ API ํธ์ถ์์ ์์ฑํด๋๋ ํจ์์ ๋งค์ฐ ์ ์ฌํ ์ฝ๋๋ฅผ ๋ฐ๋ณตํด์ ์์ฑํด์ผํ๋ค.
-
์ํ๋ ๋ฐฉํฅ:
endpoint๋ง ์ถ๊ฐํ๋ฉด ๋ฐ๋ก api๋ฅผ ์ํ๋ ๊ณณ์์ ๋ถ๋ฌ์ค๊ฒ ํ๋๊ฒ
-
ํด๊ฒฐ ๋ฐฉ๋ฒ:
generic์ ์ด์ฉํด์ ๊ธฐ์กด์ ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ์ต์ํํจ. enum์ ์ ๊ทน์ ์ผ๋ก ํ์ฉํด์ endpoint๋ง ์ถ๊ฐํ๋ฉด ๋ฐ๋ก ์ํ๋ api๋ฅผ ์ฌ์ฉํ ์ ์๊ฒํ๋ค.