๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS122

[iOS/Swift] Unit Test ๋ž€? ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ํ•˜๋ฉฐ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ธฐ๋ณธ ๋‹จ์œ„์ธ ๋ชจ๋“ˆ์„ ํ…Œ์ŠคํŠธ ํ•จ ๋ชจ๋“ˆ์ด ์ •ํ•ด์ง„ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•จ(๋ชจ๋“ˆ ๋‹จ์œ„๋กœ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋˜์–ด์•ผ ํ•จ) FIRST ๊ธฐ์ค€ Fast(๋น ๋ฆ„) ํ…Œ์ŠคํŠธ๋Š” ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•จ Independent/Isolated(๊ณ ๋ฆฝ๋จ) ํ…Œ์ŠคํŠธ๋Š” ์„œ๋กœ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•ด์„œ๋Š” ์•ˆ๋จ Repeatable(๋ฐ˜๋ณต์ ) ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์–ด์•ผ ํ•จ. ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต๊ธ‰์ž(external data Provider ๋„คํŠธ์›Œํฌ ์„œ๋น„์Šค ๋กœ์ง ๊ฐ™์€ ๊ฒƒ), ๋™์‹œ์„ฑ(concurrency) ๋ฌธ์ œ๋กœ ์ธํ•ด ๊ฐ„ํ—์  ์˜ค๋ฅ˜ ๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ์Œ Self-validating(์ž๊ฐ€ ๊ฒ€์ฆ) ํ…Œ์ŠคํŠธ๋Š” ์™„์ „ํžˆ ์ž๋™ํ™”๋˜์–ด์•ผ ํ•จ. ๋กœ๊ทธํŒŒ์ผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ํ•ด์„ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ๋Œ€ํ•ด “ํ†ต๊ณผ” ๋˜๋Š” “์‹คํŒจ”์—ฌ์•ผํ•จ Time.. 2023. 4. 10.
[RxSwift] passing viewmodel data to viewController ViewModel var currentSelectedCategoryIdx = PublishSubject() ์ดˆ๊ธฐ๊ฐ’์ด ํ•„์š”์—†๋Š” ๊ฒฝ์šฐ PublishSubject๋กœ ViewModel์— property ์ •์˜ struct Input { // ์ƒ๋žต } struct Output { let changedCategoryIdx: Observable } viewController์—์„œ ๊ฐ’์„ ๋ฐ›์•„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ Output์— ๋ฐ˜ํ™˜ํ•  Observable ํƒ€์ž…์„ ์ •์˜ํ•ด์คŒ func transform(input: Input) -> Output { let changedCategoryIdx = currentSelectedCategoryIdx.asObserver() return Output(changedCategoryIdx: change.. 2023. 3. 25.
[iOS/Swift] UICollectionView Dynamic Height with AutoConstraint UICollectionViewDelegateFlowLayout์—์„œ sizeForItemAt์— ์ •์˜ extension QuoteViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let margin = 16 + 16 let quote = viewModel?.updates[indexPath.row] return QuoteListCollectionViewCell.fittingSize(avai.. 2023. 3. 25.
[iOS/Xcode] Pod file not found ์ด์Šˆ pod deintegrate pod install ์ด์Šˆ ์ƒ๊ธด ์ด์œ  ์ถ”์ • main branch, v1.0 branch ์žˆ์„๋•Œ v1.0์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ์ถ”๊ฐ€ํ•ด ๋†“์€ ํŒŸ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์—ˆ๊ณ  main์œผ๋กœ ์˜ฎ๊ธฐ๋ฉด์„œ pod update -> ignore ์ถ”๊ฐ€ ์•ˆํ•˜๊ณ  ๋‹ค์‹œ v1.0 ํŒŸ์œผ๋กœ ์ด๋™ -> ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชป ์ฐพ์Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• main์—์„œ deintegrate ์‹œํ‚ค๊ณ  ๋‹ค์‹œ install, v1.0์—์„œ deintergrate ์‹œํ‚ค๊ณ  ๋‹ค์‹œ install 2023. 3. 23.
[Swift] FSCalendar ์ปค์Šคํ…€ ์บ˜๋ฆฐ๋” ์ •๋ฆฌ FSCalendar ์ปค์Šคํ…€ ์บ˜๋ฆฐ๋”๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ GitHub - WenchaoD/FSCalendar: A fully customizable iOS calendar library, compatible with Objective-C and Swift A fully customizable iOS calendar library, compatible with Objective-C and Swift - GitHub - WenchaoD/FSCalendar: A fully customizable iOS calendar library, compatible with Objective-C and Swift github.com ์•Œ์•„๋ณด๊ณ  ์ •๋ฆฌํ•˜๋Š”๋ฐ ๊ฝค ์˜ค๋ž˜ ๊ฑธ๋ ธ๋Š”๋ฐ ๋Œ“๊ธ€ ํ•œ๋ฒˆ์”ฉ๋งŒ ๋‹ฌ์•„์ฃผ์‹œ๋ฉด ํž˜์ด ๋ ๊ฑฐ .. 2023. 3. 16.
[Swift] ๊ณ ์ฐจ ํ•จ์ˆ˜ CompactMap ๊ธฐ์กด์˜ ์–ด๋ ˆ์ด์—์„œ ๊ฐ’์„ ๋ณ€ํ˜• ์‹œ์ผœ ์ƒˆ๋กœ์šด ์–ด๋ ˆ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด -> map ์—ฌ๊ธฐ์— nil ๊ฐ’์„ ์ œ๊ฑฐํ•œ ๊ฒƒ์ด compactMap ์ž…๋‹ˆ๋‹ค compactMap์ด ์ด์ œ๋Š” ๋„ˆ๋ฌด ๋ชธ์— ์ต์–ด๋ฒ„๋ ธ์ง€๋งŒ.. ์ •๋ฆฌ์ฐจ์›์—์„œ ์‹ค์žฌ๋กœ ์–ด๋–ป๊ฒŒ ์“ฐ์ผ ์ˆ˜ ์žˆ์„์ง€ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค!! 1. nil ์ œ๊ฑฐํ•˜๊ธฐ let test = [1, 2, nil, 4, 5, nil] let removedNilTest = test.compactMap { $0 } [1, 2, 4, 5] CompactMap์€ nil์„ ์ œ์™ธ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์˜ต์…”๋„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ํ™˜๊ฒฝ์—์„œ ํšจ๊ณผ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ•ธ๋“ค๋ง ํ•  ์ˆ˜ ์žˆ์–ด์š”! ๋งŒ์•ฝ nil ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ํ•„์š”ํ•˜๋‹ค๋ฉด map์„ ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค~ let sameWithAbove = test.compactMap { value in return.. 2023. 3. 9.
[iOS/Swift] Dynamic Link ์ •๋ฆฌ ๋กœ๊ทธ์ธ - Google ๊ณ„์ • ์ด๋ฉ”์ผ ๋˜๋Š” ํœด๋Œ€์ „ํ™” accounts.google.com Firebase > ์ฝ˜์†”๋กœ ์ด๋™ > ํ”„๋กœ์ ํŠธ ์„ ํƒ > ๋ชจ๋“  ์ œํ’ˆ > Dynamic Links ์‹œ์ž‘ํ•˜๊ธฐ ๋ฒ„ํŠผ ์„ ํƒ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ๋„๋ฉ”์ธ ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ์ž…๋ ฅํ•˜๋ฉด Google์—์„œ ์ œ๊ณตํ•˜๋Š” ๋„๋ฉ”์ธ ๋ชฉ๋ก์ด ๋œธ ๊ตฌ๊ธ€์—์„œ ์ œ๊ณตํ•˜๋Š” ๋„๋ฉ”์ธ์„ ์„ ํƒํ•˜๊ณ  ๊ณ„์† ๋ฒ„ํŠผ์„ ๋ˆ„๋ฆ„ (๊ตฌ๊ธ€์—์„œ ์ œ๊ณตํ•˜๋Š” ๋„๋ฉ”์ธ์€ xxx.page.link) ์ด๋ฏธ ๋“ฑ๋ก๋œ ์ด๋ฆ„์€ ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ๋„๋ฉ”์ธ์ธ ๊ฒฝ์šฐ ์™„๋ฃŒ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ ์ƒˆ ๋™์  ๋งํฌ ๋ฒ„ํŠผ ํด๋ฆญ ๋‹จ์ถ• URL์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•  ๋งํฌ๋ฅผ ์ง€์ •ํ•จ ์ž„์˜๋กœ ์ง€์ •๋˜๋Š” URL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋จ url ํ”„๋ฆฌํ”ฝ์Šค + ibi์— ๋ฒˆ๋“ค ์•„์ด๋””๋ฅผ ์ ์–ด์คŒ ํ‘ธ์‹œ ์„ค์ • ํ–ˆ๋‹ค๋ฉด team ID๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ์Œ App store id๋ฅผ ์ž…๋ ฅํ•˜.. 2023. 3. 8.
[iOS/Swift] Firebase Remote Notification ์ •๋ฆฌ Apple Develper Site Setting 1. APNs key ์ค€๋น„ Apple Developer ์‚ฌ์ดํŠธ > ์™ผ์ชฝ ๋ฉ”๋‰ด > Keys ์„ ํƒ Keys + ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ƒˆ๋กœ์šด ํ‚ค ๋“ฑ๋ก Apple Push Notification service ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ , key name์„ ์ง€์ • Continue ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™” ๋˜๋ฉด ๋ˆŒ๋Ÿฌ ์คŒ ์ •๋ณด๋ฅผ ํ™•์ธํ•œ ๋’ค Register์„ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ๋“ฑ๋กํ•จ 2. Key ID ํ™•์ธ ์ธ์ฆ ํ‚ค์˜ Key ID ํ™•์ธ ์ถ”ํ›„ Firebase ์„ค์ • ์‹œ ํ•„์š”ํ•จ 3. Team ID ํ™•์ธ apple developer ์ธ์ฆ์„œ, ์‹๋ณ„์ž ๋ฐ ํ”„๋กœํŒŒ์ผ ๊ด€๋ฆฌ ํŽ˜์ด์ง€์—์„œ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์— ํŒ€ ์•„์ด๋””๊ฐ€ ๋‚˜์™€ ์žˆ์ง€๋งŒ ํ•ด๋‹น ์‚ฌ์ดํŠธ๋กœ๋„ ํ™•์ธ ๊ฐ€๋Šฅ ๋กœ๊ทธ์ธ - Apple idmsa.apple.com ๋กœ๊ทธ์ธ ํ›„ ์Šคํฌ๋กค ํ•˜๊ฒŒ ๋˜๋ฉด.. 2023. 3. 8.
[iOS/Swift] Localization ์ฝ”๋“œ๋กœ ๋‹ค๊ตญ์–ด ์ฒ˜๋ฆฌ PROJECT > Info ํƒญ > Localization ์„น์…˜ > + ๋ฒ„ํŠผ ๋ˆŒ๋Ÿฌ์„œ ์–ธ์–ด ์ถ”๊ฐ€ New File > Strings File ์ถ”๊ฐ€ ์ƒ์„ฑ๋œ ํŒŒ์ผ ํ™•์ธ > ์˜ค๋ฅธ์ชฝ File ์ธ์ŠคํŽ™ํ„ฐ ํ™•์ธ Localize ๋ฒ„ํŠผ > ์–ธ์–ด ์„ ํƒ > Localize ๋ฒ„ํŠผ ํด๋ฆญ ๋‹ค์‹œ File Inspector ํ™•์ธํ•˜๋ฉด Localization ์„น์…˜ ์˜์—ญ์— ์ถ”๊ฐ€ํ•œ ์–ธ์–ด ๋ชฉ๋ก์ด ํ‘œ์‹œ๋จ ๋‹ค๊ตญ์–ด ์ฒ˜๋ฆฌํ•  ์–ธ์–ด ์„ ํƒํ•˜๋ฉด Project Navigator์—์„œ ์–ธ์–ด ๋ณ„๋กœ Localizable ์ƒ์„ฑ๋œ ๊ฒƒ ํ™•์ธ ๊ฐ€๋Šฅ ๋™์Œ์ด์˜์–ด, ํ™”๋ฉด๋ณ„๋กœ ๋ณ€์—ญ๋˜์–ด์•ผ ํ•˜๋Š” ๋‹จ์–ด๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ‚ค๊ฐ’์€ ๊ณ ์œ  ๊ฐ’์œผ๋กœ ์ž‘์„ฑํ•ด ์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Œ String Extension extension String { var localized: String { return NSL.. 2023. 2. 23.
[iOS/Swift] SafeArea ๋†’์ด ๊ตฌํ•˜๊ธฐ viewController์˜ view view.safeAreaInsets.top view.safeAreaInsets.bottom 0.0 0.0 20.0 viewDidLoad, viewWillAppear, viewDidAppear์—์„œ ๊ฐ๊ฐ view์˜ safeAreaInsets์œผ๋กœ ์ ‘๊ทผํ•œ top์˜ ๊ฒฐ๊ณผ viewDidAppear์—์„œ ์ •ํ™•ํ•œ ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ์Œ ๋Œ€๋ถ€๋ถ„ UI๋ฅผ viewDidLoad์—์„œ ์ค€๋น„ํ•˜๋ฏ€๋กœ, viewDidAppear์—์„œ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด ์•„๋‹Œ ๊ฒฝ์šฐ ์ •ํ™•ํ•œ safeArea์˜ ๋†’์ด๋ฅผ ๊ตฌํ•˜๊ธฐ ์–ด๋ ค์›€ UIApplication.shared.windows.first iOS 15.0 ๋ฏธ๋งŒ ๋ฒ„์ „์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅ UIApplication.shared.windows.first?.safeAreaInsets.top UI.. 2023. 2. 5.
[iOS/Swift] UIDevice ๋ฒ„์ „, UUID, ๊ธฐ๊ธฐ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ UIDevice current ์ธ์Šคํ„ด์Šค๋กœ ํ˜„์žฌ ๊ธฐ๊ธฐ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ ์˜ฌ ์ˆ˜ ์žˆ์Œ ์ด๋ฆ„, ๊ธฐ๊ธฐ ๋ชจ๋ธ, ์šด์˜์ฒด์ œ ์ด๋ฆ„๊ณผ ๋ฒ„์ „ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์–ป์„ ๋•Œ ์‚ฌ์šฉํ•จ ๋ฌผ๋ฆฌ์  ๋ฐฉํ–ฅ(๊ธฐ๊ธฐ๋ฅผ ๊ฐ€๋กœ, ์„ธ๋กœ๋กœ ๋‘๋Š” ๊ฒƒ)๊ณผ ๊ฐ™์€ ๊ธฐ๊ธฐ์˜ ์ƒํƒœ ๊ฐ’์˜ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ ๋ฒ ํ„ฐ๋ฆฌ ์ƒํƒœ์— ๋Œ€ํ•œ ์ •๋ณด์™€ ๊ทธ ๋ณ€ํ™”๋ฅผ ์ถ”์  ํ•  ์ˆ˜ ์žˆ์Œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๊ธฐ๋ฅผ ์žก๊ณ  ์–ผ๊ตด์— ๊ฐ€๊นŒ์ด ๊ฐ€์ ธ๊ฐ”๋Š”์ง€๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ทผ์ ‘ ์„ผ์„œ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•จ import UIKit class DeviceInfo { static var sharedObject: DeviceInfo = DeviceInfo() /// Device Name (e.g. "iPhone 14 Pro") public func getDeviceName() -> String? { return U.. 2023. 2. 4.
[iOS/Swift] infoDictionary ์•ฑ ๋ฒ„์ „, ๋ฒˆ๋“ค ์•„์ด๋”” ๊ฐ€์ ธ์˜ค๊ธฐ infoDictionary๋ž€ ํ‚ค ๊ฐ’์œผ๋กœ ๊ฐ’์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋Š” ๋”•์…”๋„ˆ๋ฆฌ ํƒ€์ž… ํƒ€๊ฒŸ > Info ํƒญ์— ์žˆ๋Š” Info.plist ํŒŒ์ผ๋กœ ๋ถ€ํ„ฐ ์ƒ์„ฑ๋˜์–ด ํ•ด๋‹น ๊ฐ’์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ์Œ infoDictionary์˜ ํ‚ค๊ฐ’์€ Info.plist์˜ Raw Keys ์œผ๋กœ ์„ค์ •ํ•ด์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ ์ฃผ๋กœ CFBundleIdentifier, NSMainNibFile, NSPrincipalClass ํ‚ค ๊ฐ’์ด ์‚ฌ์šฉ๋จ ์•ฑ ๋ฒ„์ „ ํ™•์ธ /// App Version public func getCurrentAppVersion() -> String? { guard let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return .. 2023. 2. 4.