스위프트 콤바인에서 @Published를 사용하여 계산된 속성과 동등합니까?
명령형 Swift에서는 상태를 복제하지 않고 데이터에 편리하게 액세스할 수 있도록 계산된 속성을 사용하는 것이 일반적입니다.
이 클래스가 필수 MVC용으로 만들어졌다고 가정해 보겠습니다.
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
예를 들어 Swift와 함께 사용하기 위해 Combine을 사용하여 반응형 동등물을 생성하려는 경우UI, 쉽게 추가할 수 있습니다.@Published
생성할 저장된 속성Publisher
s, 계산된 속성에는 해당되지 않습니다.
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
제가 생각할 수 있는 다양한 해결 방법이 있습니다.대신 계산된 속성을 저장하고 업데이트를 유지할 수 있습니다.
옵션 1: 속성 관찰자 사용:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
옵션 2: 사용Subscriber
내 수업에서:
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
그러나 이러한 해결 방법은 계산된 속성만큼 우아하지 않습니다.이들은 상태를 복제하며 두 속성을 동시에 업데이트하지 않습니다.
다음을 추가하는 것과 적절한 것은 무엇입니까?Publisher
콤바인의 계산된 부동산에?
다음을 기반으로 계산된 속성에 대해 아무것도 수행할 필요가 없습니다.@Published
특성.다음과 같이 사용할 수 있습니다.
class UserManager: ObservableObject {
@Published
var currentUser: User?
var userIsLoggedIn: Bool {
currentUser != nil
}
}
에서 발생하는 작업@Published
의 부동산 포장지.currentUser
그것은 부를 것입니다.objectWillChange.send()
의ObservedObject
변경시에Swift UI 보기는 다음의 속성에 상관하지 않습니다.@ObservedObject
s가 변경되었습니다. 필요한 경우 보기를 다시 계산하고 다시 그리기만 합니다.
작업 예제:
class UserManager: ObservableObject {
@Published
var currentUser: String?
var userIsLoggedIn: Bool {
currentUser != nil
}
func logOut() {
currentUser = nil
}
func logIn() {
currentUser = "Demo"
}
}
앤 어 스위프트UI 데모 보기:
struct ContentView: View {
@ObservedObject
var userManager = UserManager()
var body: some View {
VStack( spacing: 50) {
if userManager.userIsLoggedIn {
Text( "Logged in")
Button(action: userManager.logOut) {
Text("Log out")
}
} else {
Text( "Logged out")
Button(action: userManager.logIn) {
Text("Log in")
}
}
}
}
}
편집:
저는 이 대답이 장점이 있다고 생각하지만, 요즘 저는 결코 그것을 사용하지 않고 대신 @lassej가 그들의 대답에서 설명한 것과 같은 기술을 사용합니다.
먼저 고려해보고 다른 답변을 확인해보는 것이 좋습니다.
추적할 속성에 가입된 새 게시자를 만듭니다.
@Published var speed: Double = 88
lazy var canTimeTravel: AnyPublisher<Bool,Never> = {
$speed
.map({ $0 >= 88 })
.eraseToAnyPublisher()
}()
그러면 당신은 그것을 당신의 것과 매우 비슷하게 관찰할 수 있을 것입니다.@Published
소유물.
private var subscriptions = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
sourceOfTruthObject.$canTimeTravel.sink { [weak self] (canTimeTravel) in
// Do something…
})
.store(in: &subscriptions)
}
직접적인 관련은 없지만 유용한 기능을 사용하여 여러 속성을 추적할 수 있습니다.combineLatest
.
@Published var threshold: Int = 60
@Published var heartData = [Int]()
/** This publisher "observes" both `threshold` and `heartData`
and derives a value from them.
It should be updated whenever one of those values changes. */
lazy var status: AnyPublisher<Status,Never> = {
$threshold
.combineLatest($heartData)
.map({ threshold, heartData in
// Computing a "status" with the two values
Status.status(heartData: heartData, threshold: threshold)
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}()
다운스트림을 이용하는 건 어때요?
lazy var userIsLoggedInPublisher: AnyPublisher = $currentUser
.map{$0 != nil}
.eraseToAnyPublisher()
이런 방식으로 구독이 업스트림에서 요소를 가져온 다음 사용할 수 있습니다.sink
또는assign
을 하기 위하여didSet
아이디어.
Observable Object에서 PassthroughSubject를 선언할 수 있습니다.
class ReactiveUserManager1: ObservableObject {
//The PassthroughSubject provides a convenient way to adapt existing imperative code to the Combine model.
var objectWillChange = PassthroughSubject<Void,Never>()
[...]
}
그리고 @Published var의 didSet(willSet이 더 나을 수 있음)에서 send()라는 메서드를 사용합니다.
class ReactiveUserManager1: ObservableObject {
//The PassthroughSubject provides a convenient way to adapt existing imperative code to the Combine model.
var objectWillChange = PassthroughSubject<Void,Never>()
@Published private(set) var currentUser: User? {
willSet {
userIsLoggedIn = currentUser != nil
objectWillChange.send()
}
[...]
}
WWDC Data Flow Talk에서 확인하실 수 있습니다.
간단한 해결 방법:
@Published private(set) var hiddenSelectedName: String = ""
var selectedName: String {
get {
return hiddenSelectedName
}
set(newVal) {
if hiddenSelectedName != newVal {
hiddenSelectedName = newVal
// call methods and other stuff you need here...
}
}
}
}
scan(::) 현재 요소를 폐쇄에서 반환된 마지막 값과 함께 폐쇄로 제공하여 업스트림 게시자의 요소를 변환합니다.
스캔()을 사용하여 최신 및 현재 값을 가져올 수 있습니다.예:
@Published var loading: Bool = false
init() {
// subscriber connection
$loading
.scan(false) { latest, current in
if latest == false, current == true {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
}
return current
}
.sink(receiveValue: { _ in })
.store(in: &subscriptions)
}
위의 코드는 다음과 같습니다. (콤바인 수 감소)
@Published var loading: Bool = false {
didSet {
if oldValue == false, loading == true {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
}
}
}
언급URL : https://stackoverflow.com/questions/58203531/an-equivalent-to-computed-properties-using-published-in-swift-combine
'programing' 카테고리의 다른 글
iPhone X에서 UIView의 safeAreaInsets는 0입니다. (0) | 2023.08.28 |
---|---|
GPS 좌표를 위해 데이터베이스에 중요한 숫자를 몇 개 저장해야 합니까? (0) | 2023.08.23 |
VBA 코드를 사용하여 데이터 복사 및 붙여넣기 (0) | 2023.08.23 |
UITextView에 커서가 표시되지 않음 (0) | 2023.08.23 |
UICollectionView에서 레이아웃을 동적으로 설정하면 설명할 수 없는 contentOffset 변경이 발생함 (0) | 2023.08.23 |