본문 바로가기

iOS

(38)
📋 [iOS] 40-50대분들을 위한 키보드 툴바 UX 개선 경험 건설 현장에서 사용하는 업무용 앱을 개발하면서 예상치 못한 UX 문제를 발견하고 해결한 경험을 공유합니다.문제를 해결하는 과정에서 사용자 중심 설계에 대해 많이 배울 수 있었습니다.🎯 타겟 사용자 분석건설 현장의 특수성개발중이던 앱의 주요 사용자는 40-50대 건설 현장 기사님들입니다. 이분들의 특성을 분석해보니스마트폰 사용에 익숙하지 않은 경우가 많음현장에서 빠르고 간편하게 서류 작성이 필요세밀한 터치 조작보다는 직관적인 인터페이스 선호기존 종이 서류 작성 방식에 익숙함개발 초기 구현처음에는 일반적인 방식으로 구현했습니다사용자가 직접 각 칸을 터치 → 키보드 포커스 이동 → 텍스트 입력겉보기에는 문제없어 보였지만, 실제로 사용해보니 큰 문제가 있었습니다.😰 발견된 문제점직접 체험을 통한 페인 포인트..
🧊 [iOS] ImageViewer.swift 라이브러리 앱 프리징 이슈 해결 이미지 뷰어 라이브러리를 사용하던 중 특정 상황에서 앱이 완전히 멈춰버리는 문제가 발생했습니다. 문제의 원인을 추적하고 해결한 과정을 공유합니다.이 문제는 ImageViewer.swift의 내부 동작 원리를 정확히 이해해야 해결할 수 있었습니다.🚨 문제 상황재현 시나리오피드 앱에서 다음과 같은 상황이 발생했습니다:사용자 A가 이미지가 포함된 피드를 게시사용자 B가 해당 피드를 확인 중사용자 A가 피드를 삭제 (이미지도 함께 삭제됨)사용자 B가 이미지를 클릭하여 전체화면으로 보려고 시도앱이 완전히 멈춤 (Freezing 발생)작동 영상정상작동 피드 삭제 문제 상황기본 구현ImageViewer.swift 라이브러리를 사용하여 이미지 캐러셀 뷰를 구현했습니다:cell.imageView.setupImageV..
🔧 [iOS] Moya + Alamofire RequestInterceptor doNotRetryWithError 커스텀 에러 받지 못하는 문제 해결 RequestInterceptor에서 doNotRetryWithError로 전달한 커스텀 에러가 예상과 다른 에러로 변환되는 문제를 해결한 과정을 공유해보겠습니다.이 문제는 Moya와 Alamofire의 에러 래핑 구조를 정확히 이해해야 해결할 수 있었습니다.🚨 문제 상황기대했던 동작RequestInterceptor에서 리프레시 토큰이 만료된 경우, 다음과 같이 커스텀 에러를 전달했습니다:switch result {case .success: reqeustsForRetry.forEach { $0(.retry) }case .failure: // RequestRetrier에서 전달한 에러 let error = NetworkError.expired(ErrorState(status: "fail"..
🔄 [iOS] 토큰 자동 갱신 시 중복 요청 방지 구현하기 JWT 기반 인증을 구현하면서 가장 까다로운 부분 중 하나가 바로 액세스 토큰의 자동 갱신입니다.특히 동시에 여러 API 요청이 발생할 때 토큰 갱신이 중복으로 일어나는 문제는 많은 개발자들이 한 번쯤 겪어봤을 것입니다.이번 글에서는 Alamofire의 RequestInterceptor를 활용해 이 문제를 해결한 과정을 공유해보겠습니다.🤔 문제 상황 분석초기 구현의 한계점처음에는 단순하게 토큰 만료 에러를 받으면 토큰을 갱신하도록 구현했습니다:import Alamofirefinal class NetworkInterceptor: RequestInterceptor { func retry(_ request: Request, for session: Session, dueTo error: any Error..
SwiftUI에서 MVVM이 꼭 필요할까? - 1 진행했던 UIkit 프로젝트에서의 환경은MVVM(Input/Output, Action) + RxSwift + Clean Architecture를 적용했다.SwiftUI 프로젝트에서도 동일하게MVVM(Input/Output, Action) + Combine + Clean Architecture를 적용하면서 프로퍼티 관리에 대한 고민이 정말 많았다. UIKit을 사용했을 땐 ViewController 내에서는 Component 와 ViewModel, DisposeBag외엔 사용할 프로퍼티가 없었다.그 이유는 ViewController에서 어떤 Event가 발생한 경우 ViewModel에 Input Action을전달하여 로직이 작동하고 해당 로직을 통해 데이터의 변동이 일어날 경우 ViewController로..
RxSwift + TableView + MVVM + Input & Output 에서 TableViewCell 내부 disposeBag 중첩현상 📗 문제RxSwift를 공부하던 도중CustomTableViewCell을 가진 TableView를 그려보던 도중 이상한 에러가 발생한다 체크버튼을 탭 할 경우 완료 / 미완료 가 되어야하는데갑자기 두 개의 셀이 완료가 되고세 개의 셀이 미완료가 되는 것을 볼 수 있다 탭 했을 때 indexPath를 print 해보니세 개의 셀 내부 버튼을 탭했는데print는 총 6개가 찍힌 것을 볼 수 있다 그렇다,Cell 내부에 존재하는 DisposeBag 인스턴스가 재사용될 때마다중첩이 된 것이다. 그렇다면 재사용되는 타이밍에 disposeBag에 인스턴스를 새로 재할당해주면 해결이 될 것이다. 다시 빌드해봤다왜지...? 분명 disposeBag을 새로 재할당 해줬는데이상하다 싶어 print 함수를 출력해봤다. 나..
UIKit - GCD + Alert 에러 (Call must be made on main thread, Unsupported enumeration of UIWindowScene windows on non-main thread.) 📗 Call must be made on main thread 에러현재 작성된 코드guard (200.. URLSession의 dataTask 메서드를 통해 받아온 response 값의 status code 를 확인하여 에러를 가지고 있는 status code를 만나면 Alert를 뷰에 띄어준다.처음에는 기본적인 present 메서드를 호출하였고present(viewControllerToPresent: UIViewController, animated: Bool) 이 상태로 앱을 빌드한 결과아래 스크린샷으로 보여지는 에러를 만나게 됐다. 그래서 global thread에서 돌렸던 상황에서 아래 코드를 통해 main thread로 다시 thread를 main thread로 돌려놓았고 다시 빌드해 본 결과D..
UIKit에서 UICollectionView에 SkeletonView 간단하게 사용해보기 GitHub - Juanpe/SkeletonView: ☠️ An elegant way to show users that something is happening and also prepare them to which con☠️ An elegant way to show users that something is happening and also prepare them to which contents they are awaiting - Juanpe/SkeletonViewgithub.com위 링크를 통해 Xcode SPM에 라이브러리 패키지 추가 사용할 뷰컨트롤러에 SkeletonView를 importimport SkeletonView collectionView에서 사용되는 cell의 View들(compone..