我的主要问题是,我正在尝试解决SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String datetime = sdf.format(new Date(*timestamp*))
属性在通知订户有关更改之前不会更新属性值的事实(未记录)。我似乎无法解决这个问题。
请考虑以下@Published
和Subject
属性的组合。首先,一个简单的类:
@Published
然后是一个简单的直通主题:
class StringPager {
@Published var page = 1
@Published var string = ""
}
let pager = StringPager()
要调试,让我们订阅string属性并打印出来:
let stringSubject = PassthroughSubject<String, Never>()
到目前为止,一切都很好。接下来,让我们订阅主题并根据其值更改寻呼机:
pager.$string.sink { print($0) }
希望,只要我们不在首页上,此逻辑将使我们能够将寻呼机字符串大写。
现在,让我们在页面更新时通过stringSubject发送值:
stringSubject.sink { string in
if pager.page == 1 {
pager.string = string
} else {
pager.string = string.uppercased()
}
}
如果我们正确理解了此逻辑,则小写将始终为小写,而大写将始终为大写。不幸的是,这根本不会发生。这是一个示例输出:
pager.$page.sink {
$0 == 1 ? stringSubject.send("lowercase") : stringSubject.send("uppercase")
}
原因是当我们订阅主题时,我们检查pager.page = 1 // lowercase
pager.page = 2 // uppercase
pager.page = 3 // UPPERCASE
pager.page = 4 // UPPERCASE
pager.page = 1 // LOWERCASE
pager.page = 1 // lowercase
的值...但是更新pager.page
是触发主题关闭的原因,因此pager.page
不会还没有更新的值,因此主体执行了错误的分支。
我尝试通过在下沉之前pager.page
将zip
和主题一起来解决此问题:
pager.$page
以及stringSubject.zip(pager.$page).eraseToAnyPublisher().sink { ...same code... }
对其进行>
combineLatest
但这会导致观察到的行为完全相同(在前一种情况下),或者除了其他行为(在后一种情况下)之外,同样具有不想要的行为。
如何在主题stringSubject.combineLatest(pager.$page).eraseToAnyPublisher().sink { ...same code... }
内的中获得当前页面?
答案 0 :(得分:1)
根据您的意图,我了解到,我认为您希望控制页码的小写和大写。但是您在某种程度上将自己的逻辑视为 this.contactsService.getContacts().subscribe(
res=>{
console.log(res);
},
err=>{
console.log(err);
});
框架的预期工作。正如@user1046037对您的问题的评论之一提到:
Combine
与突变无关。相反,您应该使用它来随时间转换您的值。
因此,不应触发Combine
发布者的价值突变的page
订户。相反,您将故意更改string
的值。然后,您可以将值转换为绑定到string
的所需逻辑。这些逻辑应该进入对象本身。让我们看看我的意思:
page
然后,当您更改页面值时,将获得当时该字符串将保留的字符串值的预期大小写版本。
class StringPager {
@Published var page = 0
@Published var string = "lorem ipsum"
private var cancellableBag = Set<AnyCancellable>()
init() {
let publisher = $page
.map { [unowned self] in
return $0 == 1 ? self.string.lowercased() : self.string.uppercased()
}
publisher
.eraseToAnyPublisher()
.assign(to: \.string, on: self)
.store(in: &cancellableBag) // must store the subscriber to get the events
}
}
当您需要将字符串更新为除先前设置值以外的任何值时,它将独立于页面转换。这是什么意思?
let pager = StringPager()
pager.$string.sink { print($0) }
pager.page = 1 // lorem ipsum
pager.page = 2 // LOREM IPSUM
pager.page = 3 // LOREM IPSUM
pager.page = 4 // LOREM IPSUM
pager.page = 1 // lorem ipsum
pager.page = 1 // lorem ipsum
直到您有意再次设置页面:
pager.string = "new value" // new value
答案 1 :(得分:0)
问题是您实际上并没有使用Combine框架的功能。您不应该在任何值内使用sink
闭包来 look 。这颠覆了合并的全部观点!相反,您应该让该值流入{em {1}} 作为构建的Combine管道的一部分。
让我们从您的StringPager开始吧:
sink
让我们有一个具有StringPager的视图控制器类,以及一些用于设置其字符串或页码的测试按钮:
class StringPager {
@Published var page = 1
@Published var string = "this is a test"
}
您会看到此测试台的想法。我们单击第一个按钮或第二个按钮,然后将寻呼机的页面或字符串随机设置为新值,并在控制台中报告它是什么。
我们现在的目标是创建一个合并管道,该管道将在每次字符串或页面更改时根据寻呼机的页面是否为1来吐出寻呼机字符串的大写或非大写版本。我将在class ViewController: UIViewController {
let pager = StringPager()
var storage = Set<AnyCancellable>()
override func viewDidLoad() {
// ...
}
@IBAction func doButton(_ sender: Any) {
let i = Int.random(in: 1...4)
print("setting page to \(i)")
self.pager.page = i
}
@IBAction func doButton2(_ sender: Any) {
let s = ["Manny", "Moe", "Jack"].randomElement()!
print("setting string to \(s)")
self.pager.string = s
}
}
中进行操作。监视多个发布者中的任何一个更改并在其其中一个更改为viewDidLoad
时报告其当前值的Combine操作符,这就是我要使用的:
combineLatest
就是这样!现在,我将点击按钮几次,让我们看看会得到什么:
pager.$page.combineLatest(pager.$string)
.map {p,s in p > 1 ? s.uppercased() : s}
.sink {print($0)}.store(in:&storage)
如您所见,每次更改页面或字符串时,我们都会在管道末端获得一个新值,它是正确的值!如果页面大于1,则获得该字符串的大写版本;如果页面为1,则按原样获取字符串。任务完成了!