Skip to content

Instantly share code, notes, and snippets.

@jackpal
Created February 17, 2020 05:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jackpal/292b8cfbbaf9a892ccdad980d2d573c1 to your computer and use it in GitHub Desktop.
Save jackpal/292b8cfbbaf9a892ccdad980d2d573c1 to your computer and use it in GitHub Desktop.
Combine pipeline for an image board slideshow
public extension Publisher {
/// Perform requests after a delay.
func slowRequest(delay: DispatchQueue.SchedulerTimeType.Stride)
-> AnyPublisher<Self.Output, Self.Failure> {
var firstItem = true
let serialQueue = DispatchQueue(label: "slowRequest")
return self
.flatMap(maxPublishers: .max(1)) { (item: Self.Output)
-> AnyPublisher<Self.Output, Self.Failure> in
if firstItem {
firstItem = false
return Just(item)
.setFailureType(to: Self.Failure.self).eraseToAnyPublisher()
}
return Just(item)
.setFailureType(to: Self.Failure.self)
.delay(for: delay, scheduler:serialQueue).eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
/// Repeat the upstream publisher count times.
func repeating(_ count: Int) -> AnyPublisher<Self.Output, Self.Failure> {
Publishers.Sequence(sequence: 0..<count)
.flatMap(maxPublishers: .max(1)) { (_: Int)
-> AnyPublisher<Self.Output, Self.Failure> in
return self
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
}
// Publishes all the images in a board.
func publisher(board:BoardName, targetLength: CGFloat) -> AnyPublisher<UIImage?, Never> {
FourChanService.shared.posts(board:board)
.filter { post in
// Filter out posts that don't have renderable images.
guard let size = post.renderableImageSize else {
return false
}
// Filter out posts where the renderable image is too small
return min(size.width, size.height) >= targetLength/8
}
// When we get to the end of the stream, repeat.
.repeating(Int.max)
// Slow down requests to the slideshow rate.
.slowRequest(delay: 5)
.flatMap(maxPublishers: .max(1)) { post in
FourChanService.shared.dataPublisher(endpoint:post.image!)
.map {
ImageDataInContext(post:post, imageData:$0)
}
}
.tryMap {
if let image = downsample(imageData:$0.imageData, to: targetLength) {
return image
}
throw ImageLoaderError.couldNotDecodeImage
}
.map { Optional($0) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment