Skip to content

[#542] 프로젝트를 Swift 6 language mode로 전환한다#544

Closed
opficdev wants to merge 4 commits into
developfrom
chore/#542-swift6
Closed

[#542] 프로젝트를 Swift 6 language mode로 전환한다#544
opficdev wants to merge 4 commits into
developfrom
chore/#542-swift6

Conversation

@opficdev
Copy link
Copy Markdown
Owner

@opficdev opficdev commented Jun 5, 2026

🔗 연관된 이슈

🎯 의도

  • Swift 6 language mode를 적용해 TCA 도입 이후 드러나는 Sendable 및 concurrency-safety 이슈를 컴파일 단계에서 에러로 검증하기 위함

📝 작업 내용

📌 요약

  • Tuist 공통 build setting의 SWIFT_VERSION6.0으로 변경
  • PushNotificationQuery 값 타입에 Sendable 채택
  • AppDIContainer를 Alamofire의 Session.default와 유사한 default singleton 형태로 정리
  • lock으로 보호되는 DIContainer의 전역 공유 가능성을 @unchecked Sendable로 명시
  • SwiftLint trailing_comma 경고 제거

🔍 상세

  • Settings.devlog에 적용되는 공통 SWIFT_VERSION5.0에서 6.0으로 변경
  • PushNotificationQuery, SortOrder, TimeFilterSendable을 채택해 Swift 6 concurrency 진단 대응
  • 기존 AppDIContainer.sharedAppDIContainer.default로 변경해 전역 기본 인스턴스 의미를 명확화
  • 기존 NSRecursiveLock으로 보호되던 register / resolve 접근을 근거로 AppDIContainer@unchecked Sendable 채택
  • DIContainer protocol에 Sendable을 채택해 SwiftUI EnvironmentKey.defaultValue의 전역 공유 진단 대응

📸 영상 / 이미지 (Optional)

@opficdev opficdev self-assigned this Jun 5, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이번 풀 리퀘스트에서는 Project+Settings.swift 파일에서 Swift 버전을 5.0에서 6.0으로 업데이트하고, 여러 딕셔너리 및 배열 리터럴에서 불필요한 후행 쉼표(trailing comma)를 제거하였습니다. 검토할 리뷰 의견이 없으므로 추가적인 피드백은 제공하지 않습니다.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

❌ iOS CI failed.

Build failed

Compiler error lines:

/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: error: static property 'shared' is not concurrency-safe because non-'Sendable' type 'AppDIContainer' may have shared mutable state
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: error: static property 'defaultValue' is not concurrency-safe because non-'Sendable' type 'any DIContainer' may have shared mutable state
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: error: static property 'default' is not concurrency-safe because non-'Sendable' type 'PushNotificationQuery' may have shared mutable state

Tests failed

Failed schemes:

  • DevLogData (ios-test-Domain-Data)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: error: static property 'shared' is not concurrency-safe because non-'Sendable' type 'AppDIContainer' may have shared mutable state
public static let shared = AppDIContainer()
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:53:20: note: class 'AppDIContainer' does not conform to the 'Sendable' protocol
public final class AppDIContainer: DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: note: add '@MainActor' to make static property 'shared' part of global actor 'MainActor'
nonisolated(unsafe)
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: error: static property 'defaultValue' is not concurrency-safe because non-'Sendable' type 'any DIContainer' may have shared mutable state
static let defaultValue: any DIContainer = AppDIContainer.shared
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:27:17: note: protocol 'DIContainer' does not conform to the 'Sendable' protocol
public protocol DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: note: add '@MainActor' to make static property 'defaultValue' part of global actor 'MainActor'
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: error: static property 'default' is not concurrency-safe because non-'Sendable' type 'PushNotificationQuery' may have shared mutable state
public static let `default` = PushNotificationQuery(
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:10:15: note: consider making struct 'PushNotificationQuery' conform to the 'Sendable' protocol
public struct PushNotificationQuery: Equatable {
, Sendable
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: note: add '@MainActor' to make static property 'default' part of global actor 'MainActor'
  • DevLogPersistence (ios-test-Persistence-Presentation)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: error: static property 'shared' is not concurrency-safe because non-'Sendable' type 'AppDIContainer' may have shared mutable state
public static let shared = AppDIContainer()
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:53:20: note: class 'AppDIContainer' does not conform to the 'Sendable' protocol
public final class AppDIContainer: DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: note: add '@MainActor' to make static property 'shared' part of global actor 'MainActor'
nonisolated(unsafe)
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: error: static property 'defaultValue' is not concurrency-safe because non-'Sendable' type 'any DIContainer' may have shared mutable state
static let defaultValue: any DIContainer = AppDIContainer.shared
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:27:17: note: protocol 'DIContainer' does not conform to the 'Sendable' protocol
public protocol DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: note: add '@MainActor' to make static property 'defaultValue' part of global actor 'MainActor'
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: error: static property 'default' is not concurrency-safe because non-'Sendable' type 'PushNotificationQuery' may have shared mutable state
public static let `default` = PushNotificationQuery(
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:10:15: note: consider making struct 'PushNotificationQuery' conform to the 'Sendable' protocol
public struct PushNotificationQuery: Equatable {
, Sendable
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: note: add '@MainActor' to make static property 'default' part of global actor 'MainActor'
  • DevLogPresentation (ios-test-Persistence-Presentation)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: error: static property 'shared' is not concurrency-safe because non-'Sendable' type 'AppDIContainer' may have shared mutable state
public static let shared = AppDIContainer()
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:53:20: note: class 'AppDIContainer' does not conform to the 'Sendable' protocol
public final class AppDIContainer: DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: note: add '@MainActor' to make static property 'shared' part of global actor 'MainActor'
nonisolated(unsafe)
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: error: static property 'defaultValue' is not concurrency-safe because non-'Sendable' type 'any DIContainer' may have shared mutable state
static let defaultValue: any DIContainer = AppDIContainer.shared
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:27:17: note: protocol 'DIContainer' does not conform to the 'Sendable' protocol
public protocol DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: note: add '@MainActor' to make static property 'defaultValue' part of global actor 'MainActor'
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: error: static property 'default' is not concurrency-safe because non-'Sendable' type 'PushNotificationQuery' may have shared mutable state
public static let `default` = PushNotificationQuery(
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:10:15: note: consider making struct 'PushNotificationQuery' conform to the 'Sendable' protocol
public struct PushNotificationQuery: Equatable {
, Sendable
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: note: add '@MainActor' to make static property 'default' part of global actor 'MainActor'
  • DevLogWidgetCore (ios-test-WidgetCore)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: error: static property 'shared' is not concurrency-safe because non-'Sendable' type 'AppDIContainer' may have shared mutable state
public static let shared = AppDIContainer()
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:53:20: note: class 'AppDIContainer' does not conform to the 'Sendable' protocol
public final class AppDIContainer: DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:54:23: note: add '@MainActor' to make static property 'shared' part of global actor 'MainActor'
nonisolated(unsafe)
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: error: static property 'defaultValue' is not concurrency-safe because non-'Sendable' type 'any DIContainer' may have shared mutable state
static let defaultValue: any DIContainer = AppDIContainer.shared
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainer.swift:27:17: note: protocol 'DIContainer' does not conform to the 'Sendable' protocol
public protocol DIContainer {
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/DIContainerKey.swift:11:16: note: add '@MainActor' to make static property 'defaultValue' part of global actor 'MainActor'
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: error: static property 'default' is not concurrency-safe because non-'Sendable' type 'PushNotificationQuery' may have shared mutable state
public static let `default` = PushNotificationQuery(
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:10:15: note: consider making struct 'PushNotificationQuery' conform to the 'Sendable' protocol
public struct PushNotificationQuery: Equatable {
, Sendable
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogCore/Sources/PushNotificationQuery.swift:39:23: note: add '@MainActor' to make static property 'default' part of global actor 'MainActor'

Check the uploaded test log artifacts for full diagnostics.

@opficdev
Copy link
Copy Markdown
Owner Author

opficdev commented Jun 5, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project to Swift 6.0, adopts Sendable conformance for DIContainer and PushNotificationQuery, and renames AppDIContainer.shared to AppDIContainer.default. The review feedback correctly points out that the factory closure in DIContainer.register should be marked as @Sendable to ensure complete thread safety under the new protocol conformance.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

}

public protocol DIContainer {
public protocol DIContainer: Sendable {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

DIContainer 프로토콜이 Sendable을 채택함에 따라, 멀티스레드 및 서로 다른 액터 환경에서 안전하게 공유될 수 있어야 합니다.

현재 register 메서드의 factory 클로저는 @Sendable 타입이 아닙니다. 이로 인해 서로 다른 격리 컨텍스트(Isolation Context)에서 resolve를 통해 의존성을 생성할 때 데이터 레이스(Data Race)가 발생할 수 있습니다.

스레드 안전성을 보장하기 위해 구체 구현 클래스보다는 프로토콜(추상화) 수준에서 Sendable 적합성을 선언하고, DIContainer 프로토콜의 factory 클로저에 @Sendable을 추가하는 것을 권장합니다.

public protocol DIContainer: Sendable {
    func register<T>(
        _ type: T.Type,
        name: DependencyName?,
        scope: DependencyScope,
        _ factory: @escaping @Sendable () -> T
    )
    // ...
}
References
  1. Write all review comments in Korean and keep them concise and high-signal. (link)
  2. When applying Sendable conformance to ensure thread safety, prefer declaring Sendable on the protocol (abstraction) rather than the concrete implementation class.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

❌ iOS CI failed.

Build failed

Compiler error lines:

/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: error: sending 'self' risks causing data races
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: error: sending 'self' risks causing data races

Tests failed

Failed schemes:

  • DevLogData (ios-test-Domain-Data)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: error: sending 'self' risks causing data races
async let currentProvider = try await repository.fetchCurrentProvider()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: error: sending 'self' risks causing data races
async let allProviders = try await repository.fetchAllProviders()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
SwiftCompile normal arm64 /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/LinkAuthProviderUseCase.swift (in target 'DevLogDomain' from project 'DevLogDomain')
  • DevLogPersistence (ios-test-Persistence-Presentation)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: error: sending 'self' risks causing data races
async let currentProvider = try await repository.fetchCurrentProvider()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: error: sending 'self' risks causing data races
async let allProviders = try await repository.fetchAllProviders()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
SwiftCompile normal arm64 /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/LinkAuthProviderUseCase.swift (in target 'DevLogDomain' from project 'DevLogDomain')
  • DevLogPresentation (ios-test-Persistence-Presentation)
cd /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: error: sending 'self' risks causing data races
async let currentProvider = try await repository.fetchCurrentProvider()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:16:47: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
^
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: error: sending 'self' risks causing data races
async let allProviders = try await repository.fetchAllProviders()
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/FetchAuthProvidersUseCaseImpl.swift:17:44: note: sending task-isolated 'self' into async let risks causing data races between nonisolated and task-isolated uses
SwiftCompile normal arm64 /Users/runner/work/SwiftUI_DevLog/SwiftUI_DevLog/Application/DevLogDomain/Sources/UseCase/Auth/Provider/LinkAuthProviderUseCase.swift (in target 'DevLogDomain' from project 'DevLogDomain')

Check the uploaded test log artifacts for full diagnostics.

@opficdev opficdev closed this Jun 5, 2026
@opficdev opficdev deleted the chore/#542-swift6 branch June 5, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

프로젝트를 Swift 6 language mode로 전환한다

1 participant