Swiftui의 "환경"에서 영감을 얻은 종속성 관리 라이브러리.
이 라이브러리는 Brandon Williams와 Stephen Celis가 주최하는 기능 프로그래밍을 탐구하는 비디오 시리즈 인 Point-Free에 대한 많은 에피소드에 걸쳐 동기 부여 및 디자인되었습니다.
종속성은 응용 프로그램의 유형과 기능으로 제어하지 않는 외부 시스템과 상호 작용해야합니다. 이에 대한 전형적인 예는 서버에 네트워크 요청을하는 API 클라이언트뿐만 아니라 UUID
및 Date
이니셜 라이더, 파일 액세스, 사용자 기본값 및 시계 및 타이머와 같은 무해한 것들을 모두 종속성으로 생각할 수 있습니다.
의존성 관리에 대해 생각하지 않고 응용 프로그램 개발에서 실제로 멀리 떨어져있을 수 있습니다 (또는 일부 사람들은 "종속성 주입"이라고 부르지 만 결국 통제되지 않은 종속성은 코드 기반 및 개발주기에서 많은 문제를 일으킬 수 있습니다.
통제되지 않은 종속성은 파일 시스템, 네트워크 연결, 인터넷 속도, 서버 가동 시간 등과 같은 외부 세계의 미래에 취약하기 때문에 빠르게 결정적인 테스트를 작성하기가 어렵 습니다.
위치 관리자 및 음성 인식 자와 같은 Swiftui 미리보기에서는 많은 의존성이 잘 작동하지 않으며 일부는 모션 관리자 등 시뮬레이터에서도 작동하지 않습니다 . 이를 통해 해당 프레임 워크를 사용하면 기능 디자인을 쉽게 반복 할 수 없습니다.
제 3 자, 비 앱 라이브러리 (예 : Firebase, 웹 소켓 라이브러리, 네트워크 라이브러리 등)와 상호 작용하는 종속성은 헤비급이어서 컴파일하는 데 오랜 시간이 걸리는 경향이 있습니다. 이로 인해 개발주기가 느려질 수 있습니다.
이러한 이유로, 그리고 훨씬 더 많은 경우, 당신이 그들이 당신을 통제하게하기보다는 의존성을 통제하는 것이 좋습니다.
그러나 의존성을 제어하는 것은 시작일뿐입니다. 의존성을 제어하면 새로운 문제에 직면 해 있습니다.
전체 애플리케이션 전체에서 모든 곳을 명시 적으로 전달하는 것보다 더 인체 공학적이지만 글로벌 의존성을 갖는 것보다 더 안전한 방식으로 종속성을 어떻게 전파 할 수 있습니까?
응용 프로그램의 한 부분에 대한 종속성을 어떻게 대체 할 수 있습니까? 이는 온보드 경험과 같은 특정 사용자 흐름뿐만 아니라 테스트 및 Swiftui 미리보기의 종속성을 재정의하는 데 유용 할 수 있습니다.
테스트에서 기능이 사용하는 모든 종속성을 어떻게 대체 할 수 있습니까? 테스트가 일부 의존성을 조롱하지만 다른 사람이 외부 세계와 상호 작용하는 것으로 남겨 두는 것은 잘못입니다.
이 라이브러리는 위의 모든 점 을 다루고 있습니다.
라이브러리를 사용하면 자신의 종속성을 등록 할 수 있지만, 전체 목록의 DependencyValues
참조)는 즉시 사용될 수있는 좋은 가능성이 있습니다. Date()
, UUID()
, Task.sleep
또는 feature의 논리에 직접 스케줄러를 결합하는 경우 이미이 라이브러리를 사용하기 시작할 수 있습니다.
@ Observable
final class FeatureModel {
var items : [ Item ] = [ ]
@ ObservationIgnored
@ Dependency ( . continuousClock ) var clock // Controllable way to sleep a task
@ ObservationIgnored
@ Dependency ( . date . now ) var now // Controllable way to ask for current date
@ ObservationIgnored
@ Dependency ( . mainQueue ) var mainQueue // Controllable scheduling on main queue
@ ObservationIgnored
@ Dependency ( . uuid ) var uuid // Controllable UUID creation
// ...
}
Date()
, UUID()
등에 직접 연락하지 않고 종속성이 선언되면 직접 기능 모델에 정의 된 종속성을 사용할 수 있습니다.
@ Observable
final class FeatureModel {
// ...
func addButtonTapped ( ) async throws {
try await clock . sleep ( for : . seconds ( 1 ) ) // ? Don't use 'Task.sleep'
items . append (
Item (
id : uuid ( ) , // ? Don't use 'UUID()'
name : " " ,
createdAt : now // ? Don't use 'Date()'
)
)
}
}
그것이 당신의 기능에서 제어 가능한 종속성을 사용하는 데 필요한 전부입니다. 약간의 선불 작업이 완료되면 도서관의 힘을 활용할 수 있습니다.
예를 들어, 테스트에서 이러한 종속성을 쉽게 제어 할 수 있습니다. addButtonTapped
메소드 내에서 논리를 테스트하려면 withDependencies
함수를 사용하여 하나의 단일 테스트 범위에 대한 종속성을 무시할 수 있습니다. 1-2-3만큼 쉽습니다.
@ Test
func add ( ) async throws {
let model = withDependencies {
// 1️⃣ Override any dependencies that your feature uses.
$0 . clock = . immediate
$0 . date . now = Date ( timeIntervalSinceReferenceDate : 1234567890 )
$0 . uuid = . incrementing
} operation : {
// 2️⃣ Construct the feature's model
FeatureModel ( )
}
// 3️⃣ The model now executes in a controlled environment of dependencies,
// and so we can make assertions against its behavior.
try await model . addButtonTapped ( )
#expect (
model . items == [
Item (
id : UUID ( uuidString : " 00000000-0000-0000-0000-000000000000 " ) ! ,
name : " " ,
createdAt : Date ( timeIntervalSinceReferenceDate : 1234567890 )
)
]
)
}
여기서 우리는 항상 같은 날짜를 반환하기 위해 date
종속성을 제어했으며, UUID 의존성을 제어하여 호출 될 때마다 자동 증가 UUID를 반환하기 위해 uuid
의존성을 제어했으며, ImmediateClock
시간을 단일 단일로 스쿼시하기 위해 clock
의존성을 제어했습니다. 즉각적인. 이러한 종속성을 제어하지 않으면이 테스트는 Date()
및 UUID()
에 의해 반환 될 내용을 정확하게 예측할 수있는 방법이 없기 때문에 글을 쓰기가 매우 어려울 것입니다. 실제 세계 시간이 지나갈 때까지 기다려야합니다. 테스트를 느리게 만듭니다.
그러나 제어 가능한 종속성은 테스트에만 유용하지 않습니다. Xcode 미리보기에서도 사용할 수 있습니다. 위의 기능이 시계를 사용하여 시야에서 무언가가 발생하기 전에 잠금 시간 동안 잠을 자라고 가정 해 봅시다. 뷰가 어떻게 변경되는지 확인하기 위해 문자 그대로 시간을 기다리지 않으려면, .dependencies
미리보기 특성을 사용하여 클록 의존성을 "즉각적인"시계로 비난 할 수 있습니다.
#Preview (
traits : . dependencies {
$0 . continuousClock = . immediate
}
) {
// All access of '@Dependency(.continuousClock)' in this preview will
// use an immediate clock.
FeatureView ( model : FeatureModel ( ) )
}
이렇게하면 미리보기가 실행할 때 즉시 시계를 사용하도록하지만 시뮬레이터 또는 장치에서 실행할 때는 여전히 라이브 연속 ContinuousClock
사용합니다. 이를 통해 앱이 생산에서 실행되는 방식에 영향을 미치지 않고 미리보기에만 종속성을 무시할 수 있습니다.
이것이 라이브러리 사용을 시작하는 기본이지만 여전히 더 많은 일이 있습니다. 문서와 기사를 탐색하여 라이브러리에 대해 자세히 배울 수 있습니다.
빠른 시작 (위의 정보와 동일) : 모든 기능에 깊이 빠져 나가기 전에 도서관을 시작하는 기본 사항을 배우십시오.
종속성이란 무엇입니까? : 의존성이 무엇인지, 코드를 복잡하게하는 방법 및 코드를 제어하려는 이유를 알아보십시오.
종속성 사용 : 라이브러리에 등록 된 종속성을 사용하는 방법을 배우십시오.
종속성 등록 : 코드 기반의 어느 부분에서나 즉시 사용할 수 있도록 자신의 종속성을 라이브러리에 등록하는 방법을 배우십시오.
라이브, 미리보기 및 테스트 종속성 : 라이브 애플리케이션, Xcode 미리보기 및 테스트에서도 사용하기 위해 다양한 종속성 구현을 제공하는 방법을 배우십시오.
테스트 : 종속성을 제어 해야하는 주된 이유 중 하나는 더 쉽게 테스트 할 수 있도록하는 것입니다. 도서관에서 더 나은 테스트를 작성하기위한 몇 가지 팁과 요령을 배우십시오.
종속성 설계 : 의존성 설계에 대한 기술을 배우고 기능을 주입하고 테스트를 재정의하는 데 가장 유연합니다.
종속성 재정의 : 런타임에 종속성을 어떻게 변경할 수 있는지 알아보십시오. 애플리케이션의 특정 부분이 다른 종속성을 사용할 수 있습니다.
의존성 수명 : 종속성의 수명, 종속성의 수명을 연장하는 방법, 종속성이 상속되는 방법에 대해 알아보십시오.
단일 진입 지점 시스템 : "단일 진입 지점"시스템에 대해 알아보십시오.이 종속성 라이브러리에 가장 적합한 이유에 대해 알아보십시오.
우리는 파일 시스템 액세스, 타이머 및 음성 인식 API에 대한 종속성을 제어하기 위해이 라이브러리를 사용하여 Swiftui 개발을위한 현대적인 모범 사례를 사용하여 Apple의 스크럼 딩거 데모 애플리케이션을 재구성했습니다. 그 데모는 여기에서 찾을 수 있습니다.
API의 종속성에 대한 최신 문서는 여기에서 확인할 수 있습니다.
프로젝트에 패키지로 추가하여 Xcode 프로젝트에 종속성을 추가 할 수 있습니다.
https://github.com/pointfreeco/swift-dependencies
SwiftPM 프로젝트에서 종속성을 사용하려면 Package.swift
에 추가하는 것만 큼 간단합니다.
dependencies: [
. package ( url : " https://github.com/pointfreeco/swift-dependencies " , from : " 1.0.0 " )
]
그런 다음 라이브러리에 액세스 해야하는 모든 대상에 제품을 추가하십시오.
. product ( name : " Dependencies " , package : " swift-dependencies " ) ,
이 라이브러리에 대해 논의하고 싶거나 특정 문제를 해결하기 위해이를 사용하는 방법에 대한 질문이 있으시면 동료 포인트 프리 애호가와 논의 할 수있는 여러 장소가 있습니다.
이 라이브러리는 상자 밖에서 많은 종속성을 제어하지만 확장에도 열려 있습니다. 다음 프로젝트는 모두 종속성을 기반으로합니다.
Swift 커뮤니티에는 다른 많은 의존성 주입 라이브러리가 있습니다. 각각에는 종속성과 다른 우선 순위와 트레이드 오프가 있습니다. 잘 알려진 몇 가지 예는 다음과 같습니다.
이 라이브러리는 MIT 라이센스에 따라 릴리스됩니다. 자세한 내용은 라이센스를 참조하십시오.