SwiftUI中带Combine的数据流



你好👋🏼我花了10多个小时来实现Combine框架,但不能清楚地理解如何链接PublisherSubscriber。在例子中,我只想从Theme类调用setThemefunk,并自动更新Game类中的game变量。我知道如何用didSet实现它,但主要目标是用Combine实现它。谢谢你的帮助。

import SwiftUI
import Combine
import Foundation
class Theme: ObservableObject {

@Published private(set) var choosenTheme: Color? // Publisher right?

func setTheme(with color: Color?) {
if let unwrappedColor = color {
self.choosenTheme = unwrappedColor
} else {
self.choosenTheme = nil
}
}
}
class Game: ObservableObject {

@Published var game: String? // Subscriber right?
private let gameTheme = Theme()
private var cancellables = Set<AnyCancellable>()

init() {
addSubscribers()
}

private func addSubscribers() { // I think something wrong here
gameTheme.$choosenTheme
.map(createGame)
.sink { [weak self] (returnedString) in
print("Value from sink (String(describing: returnedString))")
self?.game = returnedString
}
.store(in: &cancellables)
}

private func createGame(for theme: Color?) -> String? {
if let unwrappedTheme = theme {
print(unwrappedTheme)
return String("(unwrappedTheme)")
} else {
return nil
}
}
}
// Test:
var testTheme = Theme()
testTheme.setTheme(with: .orange)
var testGame = Game()
print(testGame.game) // Should be Orange
testTheme.setTheme(with: .blue)
print(testGame.game) // Should be Blue
testTheme.setTheme(with: nil)
print(testGame.game) // Should be nil

您遇到的问题是,有两个实例的Theme

您在这里创建的一个:

// Test:
var testTheme = Theme()

Game:

中的另一个
private let gameTheme = Theme()

那么你正在改变第一个主题,并期望第二个主题改变。要解决这个问题,你必须将Theme注入到Game中。

首先,您需要对Game进行一些更改,以允许Theme被注入:

class Game: ObservableObject {

@Published var game: String?
private let gameTheme: Theme
private var cancellables = Set<AnyCancellable>()

init(gameTheme: Theme) {
self.gameTheme = gameTheme
addSubscribers()
}

接下来,你必须在测试时执行该注入:

var testTheme = Theme()
testTheme.setTheme(with: .orange)
var testGame = Game(gameTheme: testTheme)

如果你这样做,你会看到正确的变化反映在打印。

如果你愿意,你可以大大简化这两个类:

class Theme: ObservableObject {

@Published private(set) var chosenTheme: Color?

func setTheme(with color: Color?) {
chosenTheme = color
}
}
class Game: ObservableObject {

@Published var game: String?
private let gameTheme: Theme

init(gameTheme: Theme) {
self.gameTheme = gameTheme
addBindings()
}

private func addBindings() {
gameTheme
.$chosenTheme
.map(createGame)
.assign(to: &$game)
}

private func createGame(for theme: Color?) -> String? {
theme.map { $0.description }
}
}

根本不应该改变。您正在做的是设置Theme实例的主题。然而,在你的Game课上,你听的是另一个。

你现在看到问题了吗?要么用你要更新的主题初始化你的Game,要么让Theme成为singleton

试试这个:

import SwiftUI
import Combine
import Foundation
class Theme: ObservableObject {
static var shared = Theme()
@Published private(set) var choosenTheme: Color? // Publisher right?

func setTheme(with color: Color?) {
if let unwrappedColor = color {
self.choosenTheme = unwrappedColor
} else {
self.choosenTheme = nil
}
}
}
class Game: ObservableObject {

@Published var game: String? // Subscriber right?
private let gameTheme = Theme.shared
private var cancellables = Set<AnyCancellable>()

init() {
addSubscribers()
}

private func addSubscribers() { // I think something wrong here
gameTheme.$choosenTheme
.map(createGame)
.sink { [weak self] (returnedString) in
print("Value from sink (String(describing: returnedString))")
self?.game = returnedString
}
.store(in: &cancellables)
}

private func createGame(for theme: Color?) -> String? {
if let unwrappedTheme = theme {
print(unwrappedTheme)
return String("(unwrappedTheme)")
} else {
return nil
}
}
}
// Test:
var testGame = Game() //Game initialization should precede Theme because now the listener starts listening for the orange change 
var testTheme = Theme.shared
testTheme.setTheme(with: .orange)
print(testGame.game) // Should be Orange
testTheme.setTheme(with: .blue)
print(testGame.game) // Should be Blue
testTheme.setTheme(with: nil)
print(testGame.game) // Should be nil

相关内容

  • 没有找到相关文章

最新更新