강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

랑프_Langpeu님의 프로필 이미지
랑프_Langpeu

작성한 질문수

SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처

Reducer에서 사진 선택 상태 관리하기

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

작성

·

8

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

답변

답변을 기다리고 있는 질문이에요
첫번째 답변을 남겨보세요!
랑프_Langpeu님의 프로필 이미지
랑프_Langpeu

작성한 질문수

질문하기