Cannot get data in handler for when(resolved:) - ios

PromiseKit 6
I have a function that fetches a list of alerts from a db, I then need to use the contentId property on each to fetch the content and attach to the appropriate alert item.
Maybe there's a better way, but for now, what I came up with is to collect the promises into a list and call when(resolved:). I chose that because if any promise fails, I want to be able to return all those that can pass.
Inside my then handler in the function attachContents, the handler gets passed this type [Result] but the only field I can access when I map the list is isFulfilled
I checked what type Result was and found this:
public enum Result<T> {
case fulfilled(T)
case rejected(Error)
}
public extension PromiseKit.Result {
var isFulfilled: Bool {
switch self {
case .fulfilled:
return true
case .rejected:
return false
}
}
}
It led me to the first one, in Resolver.swift so I'm not sure why I can't get the data by calling fulfilled
private func getContent(for alert: Model) -> Promise<ViewModel> {
return firstly { () -> Promise<Any> in
if let contentType = AlertContentType(rawValue: alert.type) {
switch contentType {
case .news:
return newsService.get(contentId: contentId, superareaId: superareaId).compactMap { $0 as Any }
case .insight:
return insightService.get(contentId: contentId, superareaId: superareaId).compactMap { $0 as Any }
}
} else { throw DataError.Missing(error: "Could not retrieve type!") }
}.compactMap { content in
var viewModel = alert.toViewModel()
viewModel.content = content
return viewModel
}
}
private func attachContents(to alerts: [Model]) -> Promise<[ViewModel]> {
return firstly { () -> Guarantee<[Result<ViewModel>]> in
let contentPromises: [Promise<AlertViewModel>] = alerts.map {
return self.getContent(for: $0)
}
return when(resolved: contentPromises)
}.then { (vModels: [Result<ViewModel>]) -> Promise<[OIAlertViewModel]> in
vModels.map { (vm: Result<OIAlertViewModel>) in
// vm.isFulfilled is the only accessible property here
// can't call
}
}
}

Result is an enum of .fulfilled or .rejected
vModels.map { (vm: Result<OIAlertViewModel>) in
switch vm {
case .fulfilled(let alert):
print(alert)
case .rejected(let error):
print(error)
}
}

Related

Combine output of a completion handler function with a function that returns AnyPublisher of combine

I have two functions, first function gets data from a combine function. It is called as following:
self.programStorageProvider.fetchHealthPrograms()
above function has following signature:
func fetchHealthPrograms() -> AnyPublisher<[HealthProgram], Error>
Now I want to get some data from another function which itself gets data from a completion handler using above returned array([HealthProgram]), something like following:
private func updateHealthProgramStageStatus(healthPrograms: [HealthProgram]) -> AnyPublisher<VoucherIDs, Error> {
Future { [weak self] promise in
self.fetchProgramsStages { result in
var updatedHealthPrograms = [HealthProgram]()
switch result {
case .success(let stagesList):
for stage in stagesList {
// Perform some operation
updatedHealthPrograms.append(program)
}
let voucherIDsResult = VoucherIDs(all: updatedHealthPrograms, valid: [""])
promise(.success(voucherIDsResult))
case .failure(let error):
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}
I want to use it like following:
public func getVoucherIDs() -> AnyPublisher<VoucherIDs, Error> {
return self
.programStorageProvider
.fetchHealthPrograms()
.map { programs in
var healthProgramsWithVoucherId = programs.filter { $0.voucherId != nil }
return self.updateHealthProgramStageStatus(healthPrograms: healthProgramsWithVoucherId)
}
.eraseToAnyPublisher()
}
But in return line in above function I am getting following error:
Cannot convert return expression of type 'AnyPublisher<VoucherIDs, any Error>' to return type 'VoucherIDs'
How can I resolve this?
You are missing switchToLatest() after your map in your getVoucherIDs() method.
public func getVoucherIDs() -> AnyPublisher<VoucherIDs, Error> {
return self
.programStorageProvider
.fetchHealthPrograms()
.map { programs in
var healthProgramsWithVoucherId = programs.filter { $0.voucherId != nil }
return self.updateHealthProgramStageStatus(healthPrograms: healthProgramsWithVoucherId)
}
.switchToLatest()
.eraseToAnyPublisher()
}

SwiftUI - Value of type PhotosSelector has no member imageSelection

This piece of code is supposed to let the user pick an image from their device's Photo Library and print whether it was successful, successful with empty value or unsuccessful.
struct PhotosSelector: View {
#State var selectedItems: [PhotosPickerItem] = []
var body: some View {
PhotosPicker(selection: $selectedItems,
matching: .images) {
Text("Select a Photo")
}
}
func loadTransferable(from imageSelection: PhotosPickerItem) -> Progress {
return imageSelection.loadTransferable(type: Image.self) { result in
DispatchQueue.main.async {
guard imageSelection == self.imageSelection else { return }
switch result {
case .success(let image?):
print("Success.")
case .success(nil):
print("Success with empty value.")
case .failure(let error):
print("Failure.")
}
}
}
}
}
Unfortunately, guard imageSelection == self.imageSelection else { return } keeps throwing the following error:
Value of type 'PhotosSelector' has no member 'imageSelection'
I am not sure what I'm doing wrong, hope you can point me in the right direction.
Have you tried without self?
e.g.
self.imageSelection -> imageSelection
or
guard imageSelection else { return }
You are trying to use imageSelection from PhotosSelector and not from func loadTransferable.
Because PhotoSelector has no imageSelection variable.

Use the 'asPromise ()' in the Observable RxSwift can be used in a PromiseKit Promise?

I write function 'asPromise()'
extension PrimitiveSequence {
public func asPromise() -> Promise<Element> {
var disposed: Disposable? = nil
return Promise<Element> { seal in
disposed = self.asObservable()
.subscribe { event in
switch event {
case .next(let element):
seal.fulfill(element)
case .error(let error):
seal.reject(error)
case .completed:
disposed?.dispose()
}
}
}
}}
but dispose is weird, I don't better idea.
so If you have any better ideas, please let me know
thank you
There is no need to capture the disposable at all. When the Observable completes it will automatically dispose of all the resources. Here is the proper implementation:
extension PrimitiveSequence where Trait == SingleTrait { // will only work properly for Singles
public func asPromise() -> Promise<Element> {
Promise<Element> { seal in
_ = subscribe { event in
switch event {
case let .success(element):
seal.fulfill(element)
case let .failure(error):
seal.reject(error)
}
}
}
}
}
Did you know that the Combine API already has a Promise like type built in?
extension PrimitiveSequence where Trait == SingleTrait {
public func asFuture() -> Future<Element, Error> {
Future { fulfill in
_ = self.subscribe(fulfill)
}
}
}

Switch-Case Refactor in Swift With Clean Way

in this example DisplayDataItem protocol conforming by some classes (i.e DisplaySingle) it simple holds properties such as cellType, title and type. for shows a single banner, other types can be slider, carousel too.
I like to refactor createBanner() method to cleaner-way instead switch-case.
What is the protocol oriented way to do?
public enum DisplayType: String {
case Single = "Single"
func value() -> String {
return self.rawValue
}
}
protocol DisplayDataItem {
var cell: DisplayCellType { get }
var title: String { get }
var displayType: DisplayType { get }
}
final class ViewModel: NSObject {
private(set) var banner: Banner?
var item: DisplayDataItem?
init(banner: Banner? = nil) {
super.init()
self.banner = banner
}
public func createBanner() -> DisplayDataItem {
var item: DisplayDataItem?
if let banner = banner {
let displayType = DisplayType(rawValue: banner.displayType ?? "Single")
switch displayType {
case .Banner:
item = DisplaySingle(banner: banner)
case .Slider:
item = DisplaySlider(banner: banner)
case .none:
break
}
}
return item
}
I don't understand your code.
But your intention was somewhat understood.
i will make getItem method into DisplayType
public enum DisplayType: String {
case single = "Single"
case slider = "Slider"
}
extension DisplayType {
func getItem(_ banner: Banner) -> DisplayDataItem {
switch self {
case .single:
return DisplaySingle(banner: banner)
case .slider:
return DisplaySlider(banner: banner)
}
}
}
And i assumed that ViewModel have Banner?, DisplayDataItem? and Banner object have DisplayType
public func createBanner() -> DisplayDataItem? {
self.item = self.banner?.displayType.getItem(banner)
return self.item
}
You could do something like this:
extension DisplayType {
var displayDataItem: DisplayDataItem? {
switch self {
case .Banner:
return DisplaySingle(banner: banner)
case .Slider:
return DisplaySlider(banner: banner)
case default:
return nil
}
}
}
and then change your createBanner function to this:
public func createBanner() -> DisplayDataItem? {
guard let banner = banner else { return nil }
let displayType = DisplayType(rawValue: banner.displayType ?? "Single")
return displayType.displayDataItem
}
Your code wasn't compiling so you will need make some tweaks but you get the idea I think.

How to merge two observable to another observable with RxSwift?

How to merge two observable to another observable with RxSwift?
In my case:
struct ViewModel {
let items: Observable<[MediaAsset]>
init(type: Observable<AssetMediaType>, provider: DataProvider) {
items = Observable.combineLatest(type, provider.rx.didAuthorize, resultSelector: { (type, status) in
return provider.rx.fetchMedia(type)
})
}
public var didAuthorize: Observable<AuthorizeResult> {
return Observable.create { o in
//....
}
}
public func fetchMedia(_ withType: AssetMediaType) -> Observable<[MediaAsset]> {
return Observable.create { observer in
//....
}
}
but xcode build failed for reason:
Cannot convert value of type '(AssetMediaType, ) -> Observable<[MediaAsset]>' to expected argument type '(, _) -> _'
If you want them to fire at the same time you need to use a Zip
let zipped = Observalbe.zip(a, b){$0}
or in your code
struct ViewModel {
let items: Observable<[MediaAsset]>
init(type: Observable<AssetMediaType>, provider: DataProvider) {
items = Observable.zip(type, provider.rx.didAuthorize){$0}
}
Root Issue:
Your types are mismatched.
Assuming that those functions belong to DataProvider.rx, instead of ViewModel, This code:
Observable.combineLatest(type, provider.rx.didAuthorize, resultSelector: { (type, status) in
return provider.rx.fetchMedia(type)
})
returns an Observable< Observable<[ViewModel.MediaAsset]>>
but you declared items as an Observable<[MediaAsset]>
Solution: Flatten the observable
items = Observable.combineLatest(type, provider.didAuthorize) { type,status in
return (type,status)
}
.flatMapLatest { type,status in
return provider.fetchMedia(type)
}
Note: It's unclear how you are using status. I passed it to the flatMapLatest, but it won't be passed along any further.

Resources