RxSwift Observable filter with casting - swift3

Disclaimer: i'm a half Rx newbie, so it is very possible that the idea is completely bonkers :)
I'm trying to write ObservableType filter, which would pass only certain type, but will pass that type, not the original sequence type. This is what i came up with so far:
extension ObservableType where Self.E: RxFilterableType {
func filterByCast<T: RxFilterableType>(class: T.Type) -> Observable<T> {
let retval = PublishSubject<T>()
self.subscribe { event in
switch event {
case .next(let element):
if let passed = element as? T {
retval.onNext(passed)
}
case .error(let error):
retval.onError(error)
case .completed:
retval.onCompleted()
}
}
return retval
}
}
func test() {
class A: RxFilterableType {}
class B: RxFilterableType {}
let array: [RxFilterableType] = [A(), B()]
let observable: Observable<RxFilterableType> = Observable.from(array)
let observableCasted: Observable<A> = observable.filterByCast(class: A.self)
}
This has two problems: the lesser problem is that the inner subscribe disposable is not taken care of. Ideally i'd like to pass the disposal responsibility onto the return value, but i can take the disposer as parameter. I don't care.
The bigger problem is the compiler objection on the last test line:
Using 'RxFilterableType' as a concrete type conforming to protocol 'RxFilterableType' is not supported
Which means, i'm afraid, that the compiler has not enough informations to infer what i'm trying to do, despite more-than-necessary hints i've added in desperate attempts to help the poor guy.

If you put this in a playground configured to use RxSwift, it will work:
import RxSwift
extension ObservableType {
func filterByCast<T>() -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted: Observable<A> = observable.filterByCast()
_ = casted.subscribe(onNext: { print($0) })
Or if you don't like specifying the type of casted:
extension ObservableType {
func filterByCast<T>(_ class: T.Type) -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted = observable.filterByCast(A.self)
_ = casted.subscribe(onNext: { print($0) })
Requiring the class type as a parameter is a nice touch of yours. I hadn't thought of doing that.

Related

StoreKit problem while restoring In-App Purchase

I tried to find a tutorial about IAP, and I found : This one
But when I call function to restore, nothing happens. OnTap button I call : store.restorePurchase()
extension Store {
func product(for identifier: String) -> SKProduct? { ...}
func purchaseProduct (_ product: SKProduct){ ...}
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
print("restore")
}
}
restore is print in my console. It is the only thing that is printed in console. But in the code above, you can see .restored:that should print "UserIsPremium"
extension Store : SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions : [SKPaymentTransaction]){
for transaction in transactions {
var shouldFinishTransaction = false
switch transaction.transactionState{
case .purchased, .restored:
completedPurchases.append(transaction.payment.productIdentifier)
print("UserIsPremium")
shouldFinishTransaction = true
case .failed:
print("NotPremium")
shouldFinishTransaction = true
case .deferred, .purchasing:
print("...")
break
#unknown default:
print("unknown")
break
}
if shouldFinishTransaction {
print("shouldfinish")
SKPaymentQueue.default().finishTransaction(transaction)
DispatchQueue.main.async {
self.purchaseCompletionHandler?(transaction)
self.purchaseCompletionHandler = nil
}
}
}
}
}
My class Store :
import StoreKit
typealias FetchCompletionHandler = (([SKProduct]) -> Void)
typealias PurchaseCompletionHandler = ((SKPaymentTransaction?) -> Void)
class Store: NSObject, ObservableObject {
#Published var allFullVersion = [FullVersion]()
private let allProductsIdentifiers = Set ([
"ClemPapplis.OrientationEPS.versionFull"
])
private var completedPurchases = [String]() {
didSet {
DispatchQueue.main.async{ [weak self] in
guard let self = self else { return }
for index in self.allFullVersion.indices {
if self.completedPurchases.contains(self.allFullVersion[index].id){
print("completedPurchases")
UserDefaults.standard.setValue(true, forKey: "Premium")
}
self.allFullVersion[index].isLocked =
!self.completedPurchases.contains(self.allFullVersion[index].id)
}
}
}
}
I use sandbox testers.
If I buy item, no problem, datas are print in console.
Did I forget something ?
It's been a few years since I used StoreKit, but I remember having an issue with restore purchases also (and yes, #ElTomato is correct, use a separate case - I'm surprised your code actually builds). I found that the following worked:
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
delegate.purchaseComplete()
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
default:
break
}}}
}
func restorePurchases() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
for _ in queue.transactions {
alreadyPurchased = true
delegate.purchasesRestored()
}
showAlert("Purchases have been restored.")
}
Don't worry about showAlert, that's just an extension I have for UIViewController. But what I'm not seeing in your code is paymentQueueRestoreCompletedTransactionsFinished(_ queue:).
I followed the same tutorial i think you did not select the select the none in here
configuration
to restore in your simulator you should select none when you select none it tries to connect apple developers in app purchase configuration. give it a try and let me know.

SwiftUI: Why UIHostingController(rootView:) can pass nil value?

In Xcode playground :
func makeView<T:View>(v: T) -> T?{
nil
}
let v0 = makeView(v: Text(""))
let view = UIHostingController(rootView: v0)
note that func UIHostingController(rootView: ) signature not allowed pass nil value:
open class UIHostingController<Content> : UIViewController where Content : View {
public init(rootView: Content)
}
So why I can pass nil to UIHostingController(rootView:) ???
thanks ;)
Update:
So I try to write some one like class UIHostingController:
protocol P{
var name: String {get}
}
class Container<T> where T: P{
init(a:T){
print(a.name)
}
}
struct A: P {
var name:String
init?(name:String) {
if name.isEmpty{
return nil
}
self.name = name
}
}
but when I create a instance of Container, some wrong happens:
let p = Container(a: A(name: ""))
compiler complain me :
Argument type 'A?' does not conform to expected type 'P'
So how UIHostingController did that???
In the below code, it's true that V0 is nil. But, it's important to note that v0 is of type Text?.
let view = UIHostingController(rootView: v0)
Even though below line doesn't work
let view = UIHostingController(rootView: nil)
This works.
let view = UIHostingController<Text?>(rootView: nil)
To fix the error "Generic class 'Container' requires that 'A?' conform to 'P'", Container class can be updated as below
class Container<T>: X where T: P{
init(a:T?){
if let a = a {
print(a.name)
}
}
}

How to implement a type erased struct like AnyView in SwiftUI?

I'm curious about the default implementation of AnyView in SwiftUI. How to put structs with different generic types into a protocol array?
For example:
let a = AnyView(Text("hello"))
let b = AnyView(Image(systemName: "1.circle"))
let genericViews = [a, b] // No compile error
And my implementation:
struct TypeErasedView<V: View>: View {
private var _view: V
init(_ view: V) {
_view = view
}
var body: V {
_view
}
}
let a = TypeErasedView(Text("Hello"))
let b = TypeErasedView(Image(systemName: "1.circle"))
let genericViews = [a, b] // compile error
The compile error will be "Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional".
Does anyone have any ideas?
Here is a demo of possible approach. It is simplified, but shows the generic idea of how this might be done... or at least a direction.
Full compilable & working module. Tested on Xcode 11.2 / iOS 13.2
import SwiftUI
private protocol TypeErasing {
var view: Any { get }
}
private struct TypeEraser<V: View>: TypeErasing {
let orinal: V
var view: Any {
return self.orinal
}
}
public struct MyAnyView : View {
public var body: Never {
get {
fatalError("Unsupported - don't call this")
}
}
private var eraser: TypeErasing
public init<V>(_ view: V) where V : View {
eraser = TypeEraser(orinal: view)
}
fileprivate var wrappedView: Any { // << they might have here something specific
eraser.view
}
public typealias Body = Never
}
struct DemoAnyView: View {
let container: [MyAnyView]
init() {
let a = MyAnyView(Text("Hello"))
let b = MyAnyView(Image(systemName: "1.circle"))
container = [a, b]
}
var body: some View {
VStack {
// dynamically restoring types is different question and might be
// dependent on Apple's internal implementation, but here is
// just a demo that it works
container[0].wrappedView as! Text
container[1].wrappedView as! Image
}
}
}
struct DemoAnyView_Previews: PreviewProvider {
static var previews: some View {
DemoAnyView()
}
}
It's because there's a generic constraint on yours. AnyView has no generic constraint. You instantiate it with an underlying generic View, but its Body is always declared as Never. There might be compiler magic happening here as I couldn't get a generic constraint-less version to work.

Swift 3 - iterate over generic collection

I have struct Queue<T> that is based on a LinkedList<T>() I want to be able to iterate over the element in the queue and do something with them.
After doing some digging, I believe I have to inherit from Sequence and do something like this:
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}
and after I can have a function in my Queue class something like:
func iter(q: T) -> T? {
for i in q {
}
}
except the extension throws Use of undeclared type 'T' and the for loop a Type 'T' does not conform to protocol 'Sequence'
I am fairly new to Swift and I understand what I have to do I just don't know how to do it and find most explanations quite confusing. Could someone point me in the right direction?
import Foundation
public struct Queue<T> : Sequence{
fileprivate var list = LinkedList<T>()
public var queueCount : Int {
return list.getCount()
}
public var isEmpty: Bool {
return list.isEmpty
}
public mutating func enqueue(_ element: T) {
list.append(value: element)
}
public mutating func dequeue() -> T? {
guard !list.isEmpty, let element = list.first else { return nil }
list.remove(node: element)
return element.value
}
public func peek() -> T? {
return list.first?.value
}
func iter(q: T) -> T? {
for i in q {
}
}
}
extension Queue: CustomStringConvertible {
// 2
public var description: String {
// 3
return list.description
}
}
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}

swift 3, PHFetchResult.enumerateObjects error

In swift 3,the method is show me "ambiguous use of 'enumerateObjects'",what happen.how can i do?
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects { (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
}
return list
}
}
Swift 3.0: Just add the Round Brackets before Curly Brackets starts after enumerateObjects.
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects ({ (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
})
return list
}
}
Do something like this noh. You can't directly add extension for PHFetchResult because it has other ObjectType as its generic parameter PHFetchResult<ObjectType> . So you must do something else.
class FetchPhoto {
class func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
PHAssetCollection.fetchMoments(with: nil).enumerateObjects(EnumerationOptions.concurrent) { (collection, _, _) in
list.append(collection)
}
return list
}
}
PHAssetCollection.fetchMoments returns PHFetchResult<PHAssetCollection> here PHAssetCollection is the ObjectType for the PHFetchResult. You got the ambiguous error because you have not specified the objectType.
A generic way to approach this.
class FetchPhoto {
class func assetCollection<T : PHObject>(result : PHFetchResult<T>) -> [T] {
var list : [T] = []
result.enumerateObjects(EnumerationOptions.concurrent) { (object, _, _) in
list.append(object)
}
return list
}
}
Swift 3
class PhotosHelper {
class func fetchAllLocalIdentifiersOfPhotos(completion : (_ localIdentifiers : [String]) -> ()) {
let photos : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
photos.enumerateObjects ({ _,_,_ in
// Do your operations, you can see that there is no warnings/errors in this one
})
}
}