inflearn logo
강의

講義

知識共有

SwiftUI + TCA: 実戦プロジェクトで完成する次世代iOS アーキテクチャ

Reducerで写真選択状態を管理する

예제 빌드시 The compiler is unable to type-check this expression in reasonable time 에러 발생

53

icopy

投稿した質問数 21

0

//
//  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))
    }

ios swift swiftui

回答 1

0

dumveloper


The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

TCA가 복잡한 제너릭 구조가 많아서 코드가 복잡해지면 타입 추론이 길어지면서
컴파일 에러로 저런 모호한 메시지를 보여주는 경우가 많습니다.
질문자님 처럼 함수분리를 하거나 명시적으로 타입을 넣어줘서 타입추론을 쉽게 해줘야합니다.

수강 기간 연장 문의

0

24

1

수강연장부탁드려도될까요..

0

39

2

Combine Playground 에서 실행 시

0

53

2

70강 - 글로벌 액터로 격리된 Protocol 을 extension 에서 conform 시 타입 본체의 격리 수준 질문

0

41

1

searchable이 화면 하단에 위치해요

0

60

2

Drawing Cycle 관련 질문

0

72

1

앱 제작 시 주로 코드로 작성하시는 이유가 있을까요?

0

94

2

델리게이트 패턴 관련 질문

0

66

2

ios 애드몹 광고

0

126

2

수강 기간 연장 부탁드릴 수 있을까요?

0

60

1

활용 앱 57강 질문입니다.

0

54

1

그래프 그리기 위한 API가 프리미엄 요금제를 구독해야만 가능하다고 합니다...ㅜㅜ

0

75

2

과거 ai없을때 듣고 다시 듣는 중인데

1

54

1

활용 앱 30강 에러 질문입니다

0

63

2

TextField 에 Binding 으로 연결하면 에러 발생

0

52

1

xcode 26.2 에서 ReducerOf<Self> 이슈

0

76

2

20강 Task 내부에서 nonisolated async 호출 예제 질문드립니다

0

76

2

19강 Actor 의 동작 관련해 질문드립니다

0

82

2

기초앱 17강 Contraints 설정 질문입니다.

0

67

2

소리가 ㅠㅠ

0

53

2

실로폰 음원 재생 오류 해결 (do.1.wav)

1

71

2

Emulate Device Frame 관련 질문입니다.

0

88

2

해당 강의에서 나오는 노션 링크는 따로 제공안되나요 ?

0

78

2

해당 강의는 추가 오픈하는건가요?

0

96

2