MVVM 아키텍처 도입, 나의 ViewModel 사용법 (= Input-Output 패턴)
·
📱 iOS/Swift
문제 상황MVC 패턴의 코드 길어짐과 수많은 의존성을 컨트롤러가 갖는 문제,또한 View와 비즈니스 로직 분리 등을 위해 MVVM 도입했다.우리 팀에서 MVVM의 ViewModel을 어떻게 사용하기로 정의했는지 설명하겠다. 문제 해결MVVM 도입 결정MVC에선 Controller가 View와 Model 일을,MVP에선 Presenter와 View가 서로 일 주고받음MVVM에서 ViewModel은 Model하고만 소통함즉, 관심사 분리를 잘 해낼 수 있음위 특징 때문에 테스트 가능한 구조가 되어 테스팅도 가능MVC 패턴의 고질적인 컨트롤러에 많은 의존성이 쌓이는 문제를 덜어낼 수 있음 MVVM을 위한 Input-Output 패턴 도입우리팀은 Combine을 사용하여 프로젝트를 진행하고 있다.그리고 View..
Unit Test 할 때 @testable의 역할이 뭘까 ?
·
📱 iOS/Swift
문제 상황MHDomain이라는 모듈 안에 MHDomain 동적 라이브러리와 MHDomainTests 정적 라이브러리가 있고,MHPresentation 모듈 안에 MHPresentation 동적 라이브러리와 MHPresentationTests 정적 라이브러리가 있다.테스트를 진행할 때 import MHDomain 을 해주어야 하는데,이 때 import 앞에 @testable 키워드를 사용할 수 있다.무슨 역할을 하는 지 알아보자 문제 해결@testable 키워드테스트 대상 모듈의 접근 수준에 영향을 주는지 여부이다.테스트 접근성 증가:Swift의 기본 접근 수준은 internal인데, 즉 동일 모듈 내에서만 접근할 수 있는 게 기본이다.@testable import를 사용하면 테스트 모듈에서 interna..
Swift의 전역 변수 초기화 시점
·
📱 iOS/Swift
문제 상황Swift Concurrency를 공부하며 스레드 안전성을 알아보던 중 흥미로운 얘기를 듣게 되었다.let logger = Logger( subsystem: "com.example.apple-samplecode", category: "Root View")위 전역변수 logger 코드는 언제 실행될까 ??전역변수니까 시작 시점에 초기화가 될까 ?답은 아니다. 왜 그런지 알아보자 문제 해결C언어와 Objective-C의 전역변수C언어와 Objective-C에서의 전역변수는 시작 시점에 초기화가 되는 게 맞다.그러나, 프로그램 시작 시간에 아주 나쁜 영향을 줄 수 있는 동작이 된다. Swift의 전역변수Swift는 위 문제점을 인지하고 방지하고자 전역변수를 지연(= lazy) ..
디버깅할 때, print가 아닌 OSLog 사용을 위한 Logger 구현
·
📱 iOS/Swift
문제 상황우리가 평소 디버깅을 위해 흔히 사용하는 print는 간단한 출력에는 유용하지만, 다음과 같은 단점이 있다성능: print는 출력 결과를 처리하는 동안 메인 스레드에서 실행되어 앱 성능에 영향을 줄 수 있다.레벨 구분 없음: print는 메시지를 구분할 방법이 없어, 로그의 중요도(예: 디버그, 정보, 에러 등)를 명확히 알기 어렵다.릴리스 빌드에서의 노출: print는 릴리스 빌드에서도 동작하며, 개발 중 남긴 로그가 사용자에게 노출될 위험이 있다.검색 및 필터링 불편: Xcode의 콘솔에서 print 로그는 별도의 태그 없이 출력되므로 특정 로그를 찾기 어렵다.본 편에서는 print가 아닌, OSLog를 사용하기 위해 어떻게 Logger를 구현했는지 알아보겠다! 문제 해결개요OSLog는 pr..
[iOS] SwiftLint 적용 & 겪은 에러 해결과정 기록
·
📱 iOS/Swift
SwiftLint 적용 & 겪은 에러 해결과정 기록첫 번째 에러: '.xcfilelist'처음 에러는 .xcfilelist에서 겪었는데, Secret.xcconfig로만 설정해둬서 발생한 문제였다.아래와 같이 Secret.xcconfig 하나, Pods-IssueTracker.debug.xcconfig하나 각각 넣어줘서 해결했다. (맞는지 모르겠음...)  두 번째 에러: SandBoxBuild Settings - User Script Sandboxing을 No로 설정해서 보안 이슈 만들어버림 (나중에 처리) 세 번째 에러: Lint 적용이 이상함excluded에 Pods를 넣어도 계속 외부 라이브러리의 lint까지 에러를 잡았는데, 이건 캐시 문제인지..? swiftlint clear-cache 명령을..
[Swift] struct가 아닌, enum으로 네임스페이스 관리하기
·
📱 iOS/Swift
학습 목표네임스페이스 개념 이해하기struct로 네임스페이스를 만든다면 ?enum으로 네임스페이스 만들기학습 내용네임스페이스란 ?프로젝트를 하다가 static func 정적 메소드를 사용할 일이 많아져서 정적 메소드를 모아둔 구조체를 만들어서 관리하고자 했다.이를 Swift에서는 네임스페이스라 부르는 듯하다. (관련된 애들을 묶어놓는 단위)그러나, 아래 코드는 문제를 가지고 있다.struct RandomUtil { static func generateSize() -> Size { let width = Double.random(in: 100...150) let height = Double.random(in: 100...150) return Size(width: w..
[CoreFoundation] CGPoint, CGSize, CGRect
·
📱 iOS/Swift
학습 목표View의 위치와 크기를 결정하는 방법CGPointCGSizeCGRect학습 내용View 위치와 크기 결정뷰를 그리기 위해선, 위치 결정이 필요함뷰 위치 결정에서 CG로 시작하는 3가지가 필수적인 존재뷰컨트롤러에 있는 self.view (화면에서의 루트 뷰)를 제외한나머지 SubView들은 모두 부모 UIView의 좌측 최상단에서 (x, y)만큼 움직여 그림또한, 어느 크기만큼 그릴 것인지에 대한 Width, Height가 필요함x, y, width, height 모두가 필요한데, CG로 시작하는 3가지가 이 4개를 다루고 있음(x, y) 좌표를 설정하는 CGPointCoreFoundation - CFCGTypesCGPoint 구조체는 x, y를 갖고 있다.소들님 블로그 봤을 땐, x와 y의 타..
[Swift] async-await & concurrency
·
📱 iOS/Swift
공부한 개념💡 Task, async-awaitTaskSwift - Swift Standard Library - Concurrency - Task공식문서 링크비동기 작업 단위작업을 넣어주면 백그라운드에서 작업 수행 가능한 비동기 컨텍스트 사용Swift 5.5 버전 때, async-await와 같이 등장했고 함께 쓰임DispathcQueue.global().async와는 조금 다름 (밑에서 설명)Task vs DispatchQueue.global().async둘 다 비동기 스레드에 작업을 할당해서 처리하는 역할 + 스레드 풀 방식둘은 뭐가 다를까 ?TaskSwift에 포함별도의 import 필요 XConcurrency 모델의 고수준 모델[중요] 동기 함수에서 async-await 사용 시, Task 클로저..
[Swift] LocalizedError 프로토콜로 예외처리 잘하기
·
📱 iOS/Swift
공부한 개념catch문 덕지덕지일 때 해결하기LocalizedError (errorDescription, failureReason, recoverySuggetsion, helpAnchor)error.localizedDescriptionenum MyError: LocalizedError { case alreadyInit // 이미 Init 했음 case mkdirFailed // 디렉토리 생성 실패 case readFailed // 파일 내용읽기 실패 case writeFailed // 파일 쓰기 실패 case compressionFailed // 압축실패 case hashingFailed // 해싱하기 실패}case .Init: do { try myini..
[Swift] Swift로 알아보는 동기-비동기 / 동시-직렬
·
📱 iOS/Swift
공부한 개념동기-비동기 / 동시-직렬Serial 직렬작업을 다른 한 개의 스레드에서 처리함직렬 큐에 담긴 작업은 오직 하나의 스레드에만 분배작업 시작과 종료가 예측이 가능→ 순서가 중요한 작업을 처리할 때 사용let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)// 동시성을 지원하는 큐// 동기적으로 실행concurrentQueue.sync { print("동기 작업 1 시작") sleep(2) // 작업이 2초간 실행 print("동기 작업 1 완료")}concurrentQueue.sync { print("동기 작업 2 시작") sleep(1) // 작업이 1초간 실행 ..