묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
예제 빌드시 The compiler is unable to type-check this expression in reasonable time 에러 발생
// // EditImageReducer.swift // AppStore // // Created by Langpeu on 12/24/25. // import ComposableArchitecture import SwiftUI import SwiftData import Photos @Reducer struct EditImageReducer { @ObservableState struct State { var userImage: Image? var assets: [PHAsset] = [] var selectedPhoto: (id: String, data: Data)? @Presents var alert: AlertState<Action>? } enum Action { case onAppear(image: Data?) case setUserImageData(Data?) case setUserImage(Image) case authResult(Bool) case onSelectPhoto(id: String, data: Data) } var body: some Reducer<State, Action> { Reduce { state, action in switch action { case let .onAppear(imageData): return Effect.run { send in let isAuth = await PhotoManager.requestAuthorization() await send(.authResult(isAuth)) await send(.setUserImageData(imageData)) } case let .authResult(isAuth): if isAuth { let assets = PhotoManager.getAssets() state.assets = assets } else { state.alert = AlertState.creatAlert(type: .error(message: "권한이 없습니다")) } case let .setUserImageData(data): guard let data, let uiImage = UIImage(data: data) else { return .none } return .send(.setUserImage(Image(uiImage: uiImage))) case let .setUserImage(image): state.userImage = image return .none case let .onSelectPhoto(id, data): state.selectedPhoto = (id: id, data: data) } return .none } } } struct EditImageView: View { @Bindable var store: StoreOf<EditImageReducer> let colums: [GridItem] = .init(repeating: .init(.flexible()), count: 3) @Query private var users: [User] private var user: User? { users.first } var body: some View { ScrollView { VStack { Text("선택된 이미지") // 선택된 이미지 Group { if let image = store.userImage { image .resizable() .scaledToFit() } else { Color.gray.opacity(0.2) } } .frame(width: 100, height: 100) .clipped() .cornerRadius(8) } LazyVGrid(columns: colums, spacing: 10) { ForEach(store.assets, id: \.localIdentifier) { asset in let isSelectedImage = store.selectedPhoto.id == asset.localIdentifier AssetImageView(asset: asset, isSelected: isSelectedImage, onTap: { data in //TODO: onTap store.send(.onSelectPhoto(id: asset.localIdentifier, data: data)) }) .clipped() .clipShape(RoundedRectangle(cornerRadius: 8)) } } .padding(8) } .onAppear() { store.send(.onAppear(image: user?.imageData)) } } } private struct AssetImageView: View { let asset: PHAsset let isSelected: Bool let onTap: (Data) -> Void let imageWidth: CGFloat = (UIScreen.currentWidth - 16 - 20) / 3 @State private var uiImage: UIImage? = nil var body: some View { Group { if let uiImage = uiImage { Image(uiImage: uiImage) .resizable() .scaledToFill() .onTapGesture { if let data = uiImage.jpegData(compressionQuality: 1.0) { onTap(data) } } } else { Color.gray.opacity(0.2) } } .frame(width: imageWidth, height: imageWidth) .overlay(alignment: .topTrailing, content: { if isSelected { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) .frame(width: 20, height: 20) } }) .onAppear() { PhotoManager.fetchImage(asset: asset) { uiImage in self.uiImage = uiImage } } } }75번라인var body: some View 에서 아래 빌드에러 발생The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions----위와 같은 에러로 빌드가 안되서아래 처럼 처리 했습니다.(Xcode26.2 + M1Pro Macbook) 포인트: let isSelectedImage = ... 같은 로컬 let + 복잡한 클로저를 ForEach 내부에서 직접 쓰면 타입체커가 더 힘들어해. 함수로 빼면 거의 항상 해결됨. LazyVGrid(columns: colums, spacing: 10) { ForEach(store.assets, id: \.localIdentifier) { asset in assetCell(asset) } } .padding(8) } .onAppear() { store.send(.onAppear(image: user?.imageData)) } } @ViewBuilder private func assetCell(_ asset: PHAsset) -> some View { let isSelectedImage = (store.selectedPhoto?.id == asset.localIdentifier) AssetImageView( asset: asset, isSelected: isSelectedImage, onTap: { data in store.send(.onSelectPhoto(id: asset.localIdentifier, data: data)) }) .clipped() .clipShape(RoundedRectangle(cornerRadius: 8)) }
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
TextField 에 Binding 으로 연결하면 에러 발생
강의 내용중에아래 2가지가 가능하다고 했는데1번TextField("이메일을 입력해주세요", text: $store.email.sending(\.inputEmail))2번 TextField("이메일을 입력해주세요", text: Binding(get: { store.email }, set: { email in store.send(.inputEmail(email)) }))2번 코드로 작성후 이메일주소를 수정후저장 하니깐 아래와 같은 에러가 발생했습니다.A "forEach" at "AppStore/MyPageReducer.swift:96" received an action for a missing element. Action: MyPageStackReducer.Action.email(.inputEmail) This is generally considered an application logic error, and can happen for a few reasons: A parent reducer removed an element with this ID before this reducer ran. This reducer must run before any other reducer removes an element, which ensures that element reducers can handle their actions while their state is still available. An in-flight effect emitted this action when state contained no element at this ID. While it may be perfectly reasonable to ignore this action, consider canceling the associated effect before an element is removed, especially if it is a long-living effect. This action was sent to the store while its state contained no element at this ID. To fix this make sure that actions for this reducer can only be sent from a store when its state contains an element at this id. In SwiftUI applications, use "NavigationStack.init(path:)" with a binding to a store.확인해 보니 pop시 Binding의 set 이 호출되서이미 path에 없는 element를 호출하게 되서해당 에러가 발생한다고 합니다.그럼 TCA 환경에서는 1번을 꼭 사용해야 하는 건지요 ?
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
xcode 26.2 에서 ReducerOf<Self> 이슈
강의 내용처럼EditNameReducer 에서ReducerOf<Self> 를 사용하면아래처럼 에러가 발생합니다.@Reducer 매크로 타입은 빌드시점이 되야EditNameReducer 가 Reducer 타입인걸알기 때문에 사용 못한다고 하는데 강의는 에러 없이 되서 xcode26.2 에서왜 안되는지 알고 싶습니다.결국 아래처럼 변경해서 사용 했습니다.var body: some Reducer <State, Action> { Reduce { state, action in return .none } }
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
20강 Task 내부에서 nonisolated async 호출 예제 질문드립니다
18분 55초 쯤 예시를 보여주시면서/// 2) (액터 외부) 동기 코드가 (일반) 비동기 코드에서 불려지면.. 어떤 (컨텍스트에 해당하는) 작업(Task) 내부에서 실행됨 ===> Task 격리 func readAny(with books: [Book]) async { if let book = books.randomElement() { readBook(with: book) } } Task { let account = LibraryAccount(idNumber: 1) /// 액터 let books = await account.booksOnLoan /// 액터내부의 (상태)값 가져오기 (Sendable타입) await readAny(with: books) /// 액터 외부에서 실행 (일반적으로 이런 구현은 옳지 않음) }위와 같은 코드에서 Task 가 3번 스레드에서 돌아가고 있었다고 치면, account.booksOnLoan 을 가져오는건 2번 스레드로 갈 수 있다고 쳐도, await readAny(with:) 는 다시 Task 와 동일한 3번 스레드에서 돌아가게 될거다! 라고 말씀을 주셨는데요사실 nonisolated async func 인 readAny 를 await 로 호출을 하는 순간, 해당 함수는 Task 의 스레드는 무관한게 아무 스레드에서나 돌아갈 수 있다고 생각을 했습니다 (실제로 Task 를 mainActor context 로 두고 해당 코드를 돌려봐도 readyAny 는 main 에서 돌아가지 않았구여!)이해 편의상 돌려서 설명을 주시건가??? 생각이 들지만 혹시 정확히 어떤 의미로 요렇게 말씀을 주신건지 궁금합니다~감사합니다저장
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
19강 Actor 의 동작 관련해 질문드립니다
안녕하세요 앨런님! 19강에서 actor 를 이해 편의상 "특정한 하나의 스레드에서만 실행"되는 것처럼 그림을 첨부해주셨는데요 (물론 설명으로는 한번에 하나씩 실행되는건 맞지만 스레드를 특정할 수는 없다" 라고 말씀주셨지만요)그러면 이해 편의상이 아니라, 좀 더 정확하게는 제가 이해한 내용이 맞을지 한번 확인 부탁드려도 될까요?항상 좋은 강의 감사합니다. 기본적으로 actor 의 실행 스레드는 고정되어있지 않음Actor 의 메서드나 데이터에 접근하면 serial executor 에 작업이 쌓일 것이때 actor 는 선입 선출로 동작하지 않으므로, 우선 순위에 따라 실행 순서는 달라질 수 있음Actor 의 작업이 실행될때 어느 스레드에서 실행할건지는 상관없고 중요하지도 않음. 한 작업이 끝나고 나면, serial executor 에서 대기하고 있던 작업은 어느 스레드에서든 heap 에 저장된 데이터 읽어서, 그 데이터 기반으로 작업하면 되는 것.중요한건 데이터 경쟁이 발생하지 않게 serial executor 에 넣어서 actor 관련 작업은 한번에 하나의 스레드에서만 돌게 하는 매커니즘
-
미해결[Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기
소리가 ㅠㅠ
한쪽에서만 들려요 ㅠㅠ
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
Actor에서 Task vs Task.detached 사용 시 재진입 문제 질문
actor TaskImageDownloader { /// (다운로드) 상태를 저장하기 위한 열거형 정의 enum DownloadState { case completed(UIImage) case loading(Task<UIImage, Error>) case failed } private(set) var cache: [String: DownloadState] = [:] func image(from url: String) async throws -> UIImage { /// 기존에 저장된 상태가 있는 경우 (캐시 먼저 확인) if let cachedState = cache[url] { switch cachedState { case .completed(let image): /// 이미지 리턴 return image case .loading(let task): /// 작업(Task)을 기다렸다가 ===> 이미지 리턴 return try await task.value case .failed: throw "이미지 다운로드 실패" } } /// 작업(Task)을 생성 let task = Task.detached<UIImage, Error> { let image = try await downloadImage(from: url) return image } /// 일단 (완료되지 않은) 작업 상태를 보관 cache[url] = .loading(task) do { /// 작업의 완료를 기다렸다가 ===> 완료되면 ===> 완료상태(이미지)로 바꿔서 보관 let image = try await task.value cache[url] = .completed(image) return image } catch { cache[url] = .failed // 에러 발생의 경우 throw "이미지 다운로드 실패" } } } Actor에서 Task vs Task.detached 사용 시 재진입 문제 질문안녕하세요.위 코드에서 Task.detached 대신 일반 Task {}를 사용하면 재진입 문제가 발생할 수 있을 것 같아 질문드립니다.제가 이해한 바로는:Task {}를 사용하면 생성된 작업이 actor의 context를 상속받아 serial executor에서 실행됩니다다운로드 작업 중 await를 만나면 actor가 suspension되고, 이 시점에 다른 스레드에서 해당 actor에 재진입할 수 있습니다따라서 동일한 URL에 대해 중복으로 다운로드 작업이 생성될 가능성이 있습니다반면 Task.detached를 사용하면 actor context와 독립적으로 실행되어 이러한 재진입 문제를 방지할 수 있는 것으로 이해했습니다.제가 이해한 내용이 맞는지 확인 부탁드립니다. 감사합니다.
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
해당 강의에서 나오는 노션 링크는 따로 제공안되나요 ?
해당 강의에서 나오는 노션 링크는 따로 제공안되나요 ?
-
해결됨Combine - iOS의 Reactive Programming(2025)
강의자료 문의
안녕하세요, 강의자료는 코드 외에 pdf 파일은 제공받을 수 있을까요?
-
해결됨[Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기
index와 indexSet
fruitArray 를 delete하는 함수에서index가 아닌 indexSet과 remove(at:)이 아닌 remove(atOffsets:)를 사용하셨는데 왜 그렇게 하셨는지 궁금합니다!
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
18강 NSCache 예시 질문
안녕하세요!18강 전체적으로 cache 에 값을 세팅하기 전후로 lock 이나 semaphore 등을 이용해서 GCD 에서 thread safe 를 구현하는 방법에 대한 예시를 들어준것 확인했습니다이때 removeAll() 의 앞뒤로는 따로 처리가 되어있지않아서, 정말 필요가 없어서 작성되어있지 않은건지, 아니면 단순 예시라서 여기까지는 적용하지 않은건지 궁금합니다func clearAll() { cache.removeAll() } 두번째로, 이건 주제와 좀 동떨어진 질문이긴한데요..! (혹시 강의 성격과 너무 벗어났다 싶으면 무시해주세요!)예시를 NSCache 로 들어주셨는데, NSCache 문서에 보면 캐시를 직접 잠그지 않고도 다양한 스레드에서 캐시에 항목을 추가, 제거 및 쿼리할 수 있다고 나와있는걸 확인했습니다.You can add, remove, and query items in the cache from different threads without having to lock the cache yourself즉, 문서를 통해 자체적으로 thread safe 하게 뭔가를 하고 있구나..를 유추할 수 있기 때문에 (내부적으로는 NSLock 을 사용하고 있긴하지만요) 이럴때는 저희쪽에서 @unchecked Sendable 만 사용해도 무방할까요?하지만 이런 문서 등을 확인하지 않는 이상 정확히 NSCache 가 thread safe 하게 뭔가를 처리한다는걸 확인할 수 없기 때문에, 예시처럼 @uncheked Sendable 명시와 동시에 자체적으로 semaphore 나 lock 을 걸어줘야하는건지..? 앨런님은 어떻게 생각하시는지 궁금합니다!감사합니다~
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
Task 클로저 내 `non-Sendable` 값 타입 접근 시, 캡처 리스트가 정의된 Task 순서에 따른 컴파일러 에러 차이
12강 16분 즘에, Task 의 클로저에 value type 의 프로퍼티를 캡처 리스트로 명시하면 아래와 같은 코드에서는 에러가 나지 않는다고 되어있습니다.struct ValueCounter { var value = 0 mutating func increment() -> Int { value = value + 1 return value } } func test() { var valueCounter = ValueCounter() Task { print(valueCounter.increment()) print(valueCounter.value) } Task { [valueCounter] in var newValueCounter = valueCounter print(newValueCounter.increment()) print(newValueCounter.value) } }하지만 제가 Xcode 26.0.1 에서 확인했을때는 해당 코드의 첫번째 Task 에서 다음과 같은 컴파일 에러가 발생했습니다Sending value of non-Sendable type '() async -> ()' risks causing data races이에 추가로 이것 저것 확인해보다가, 아래와 같이 캡처 리스트를 사용하는 Task 를 먼저 작성하면, 에러가 발생하지 않는것을 확인했습니다.// 캡처 리스트 사용하는 Task 순서 변경하니 정상 func test2() { var valueCounter = ValueCounter() Task { [valueCounter] in var newValueCounter = valueCounter print(newValueCounter.increment()) print(newValueCounter.value) } Task { print(valueCounter.increment()) print(valueCounter.value) } }이와 같은 현상을 어떻게 설명할 수 있을지 궁금합니다.첫번째 예시의 두번째 Task 에서는 [valueCounter] in 으로 현재 값을 캡처하려고해도, 이미 첫번째로 정의된 Task 에서 valueCounter.increment() 를 호출하면서 다른 스레드 (편의상) 에서 값을 변경하고 있기 때문에, 동일 시점에 딱 한개의 쓰레드에서의 접근이 깨져서 이런 에러가 발생하는 걸까요? (그렇다기엔 에러 위치는 첫번째 Task 정의에서 떠서... 아닌가 싶기도하고요..)두번째 예시의 첫번째 Task 에서는 캡처 리스트로 값을 캡처해서 valueCounter.increment() 를 호출하고, 두번째 Task 는 valueCounter.increment() 를 하려고해도 이 시점에서 valueCounter 를 참조하고 있는건 이곳 뿐이기 때문에 (첫번째 Task 에서는 캡처해서 사용), 동일 시점에 딱 한개의 쓰레드에서의 접근이 보장되어서 에러가 발생하지 않는걸까요? 결과를 기준으로 나름대로 고민을 해봤는데, 어쨌든 다 추측이라서.. 혹시 이와 같은 현상을 어떻게 이해하면 될지 궁금합니다감사합니다.
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
확인 버튼 만들 때 폰트 색상 화이트 주는 위치 문의드려요.
강의에 보면 확인버튼의 폰트 색상을 화이트로 줄 때 .background() 부분의 상단 또는 하단 측에 스타일을 줘도 된다고 하셨는데요. 하단 측에 .foregroundStyle(Color.white)를 주게 되면 확인 글자가 나타나지 않더라구요. 상단에만 줘야하는 것 아닌지 여쭤 봅니다.
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
Button(action: label:)이 강의와 달라요!
강의를 보면 Button(action: {}, label: {})로 나오는데요. 저는 같은 것을 선택했는데 아래와 같이 나오면서 에러(?)가 뜹니다.제미나이에 물어봐도 제 수준에서는 답변이 이해가 가지 않아 질문 드립니다.
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-1)
Task 의 default 우선순위 문의 (utility vs medium)
안녕하세요 3강 19:34 쯤에 Task 의 default 우선순위를 utility 로 말씀주셨는데, 제가 확인해봤을때는 medium 인 듯해서.. medium 이 맞을까요?Task.detached { print(Task.currentPriority) } // TaskPriority.mediumTaskPriority 의 구현부를 볼때도 default 가 medium 으로 되어있는것 같구요! @available(*, deprecated, renamed: "medium") public static let `default`: TaskPriority별로 중요한건 아니구, 나중에 한번 더 언급해주실거라고 하셔서 1부를 끝까지 들어봤는데, 별도로 말씀주시는 내용이 없는 듯 하여 가볍게 질문드립니다~감사합니다
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
HStack과 VStack에서 alignment와 spacing 방향이 이해가 잘 안갑니다.
스킬 창 아래 부분은 HStack에 VStack(앉아, 엎드려, 하우스)과 VStack(손, 코) 두개의 VStack 구조로 되어 있잖아요.여기서 alignment와 spacing 방향이 너무 혼동됩니다. 처음에 간단한 구조는 이해하는 것 같았거든요. 살짝 구조가 복잡해지니까 혼동됩니다. 우선 제가 이해하고 있는 부분은 아래와 같습니다.HStack은 수평 구조이기 때문에 alignment는 top, center, bottom이고 spacing은 수평 간격이 조정되고 VStack은 수직 구조이기 때문에 alignment는 leading, center, tailing이고 spacing은 수직 간격이 조정 되는 것이 맞을까요?혹시 맞다면 이 정도만 이해하고 있으면 될까요?이 부분의 대한 설명 좀 부탁드려도 될까요?
-
미해결[Lv.3] 실전 네트워크 통신 - SwiftUI Combine, Async/Await
@Published의 용도
안녕하세요이번 실전 네트워크 통신 강의를 들으며 ViewModel을 만들 때 1번 방법이 아닌 2번 방법으로 만들 수 있다는 사실을 알게되었습니다. 앞쪽 강의에서 기존 1번 방식에서는 변수 생성 시 @Published 를 붙여야했지만, 2번 방식처럼 @Observable을 사용하면 @Published를 붙이지 않아도 된다고 하셔서 결국 위의 두 코드는 같은 기능을 한다고 이해를 했습니다. 그런데 이번 강의에서 아래와 같이 @Published를 사용하여서, 이 부분이 이해가 잘 가지 않습니다 혼자 고민해본 결과로는, - count 변수: 단순히 값을 읽고 쓰는 용도- textFieldID: 퍼블리셔(특정 시간/조건에 따라 값을 방출하는..?)로 사용 위의 용도 차이이다. 그렇다면 기존 방식에서 @Published를 모두 붙여야했던 이유는 무엇인지..? 라는 질문이 또 생기는 것 같습니다. 답변 주시면 감사하겠습니다!!
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
modifiers 창이 안보여요.
xcode 26.0.1 사용 중인데요.inspectors 창은 열려 있는데요.modifiers 창이 없고 이미지와 같이 3개만 보여요.이럴 땐 어떻게 해야할까요?
-
미해결앱 개발 기초부터 실전까지 하나로 끝내는 Swift & iOS 마스터 클래스
430강 modularization
안녕하십니까? 강사님명절은 잘보내셨는지요?실습중에 오류가 나는 부분이 있어 질문 드립니다.430강 shiny day를 모듈화시키는 강의를 실습하던중에 오류가 나서 질문 드립니다.ShinyModel, ShinyService, ShinyFormatter를 패키지화 시켰고,access level도 수정했습니다.그런데도 오류가 납니다. Xcode 26에서 코딩했고, 오류부분의 일부분은 chatgpt도움을 받아 수정하였습니다.혹시 몰라 전체 소스를 이메일로 보내드립니다.바쁘시더라도 확인 부탁드립니다.
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-1)
18강 자식 작업의 메타데이터 상속 관련 강의 자료 문의
안녕하세요~18강 8:35 쯤 강의자료에 자식 작업은 부모작업의우선순위 실행중인액터로컬변수를 상속한다고 되어있는데 이때 실행중인 액터에 취소선이 그어져있는 이유가 있을까요?혹시 의미가 있는건지...궁금합니다감사합니다