How to create a generic completion closure? - swift

I have the following protocol:
protocol RESTAPIprotocol {
associatedtype T: Object, Decodable
}
extension RESTAPIprotocol {
func getList(sinceSyncToken: String = "",
pageLimit: Int = 100,
progress: Moya.ProgressBlock? = nil,
completion:#escaping (_ list: [T]?, _ error: AppError?) -> Void) { ... }
and object(s):
final class RLMOrganization: RLMDefaults {
typealias T = RLMOrganization
}
final class RLMProject: RLMDefaults {
typealias T = RLMProject
}
final class RLMLocation: RLMDefaults {
typealias T = RLMLocation
}
wanting to use it like this:
class SyncEngine {
let listCompletionClosure = { (_ list: [T]?, _ error: AppError?) -> Void in ... }
func syncOrganizations() {
// Sync down from server and update our local DB.
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
But get the error:
Which sort of makes sense, but don't understand how I can pass along the generic used as part of RESTAPIprotocol into a generic closure?
The goal is to try to accomplish the following:
func syncOrganizations() {
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
func syncProjects() {
projectsDAL.getList(sinceSyncToken: projectsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
func syncLocations() {
locationsDAL.getList(sinceSyncToken: locationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}

How about change it to"
let listCompletionClosure = { (_ list: [RLMOrganization]?, _ error: AppError?) -> Void in ... }
The completion handle requires a concrete type and you have defined T = RLMOrganization within the context of the RLMOrganization class.
Edit: a closure can’t be generic but a function can:
func listCompletion<T: Decodable>(list: [T]?, error: AppError?) {
// you must cast `list` to a concrete type
}
func syncOrganizations() {
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletion)
}
func syncProjects() {
projectsDAL.getList(sinceSyncToken: projectsDAL.getLastSyncToken(), completion: listCompletion)
}
func syncLocations() {
locationsDAL.getList(sinceSyncToken: locationsDAL.getLastSyncToken(), completion: listCompletion)
}

Related

Swift: Can I have a protocol that inherits from a protocol and constrains it?

Say I have the following protocols:
protocol RateableItem {
var identifier: String { get } // placeholder. This could be a lot of properties
var name: String { get set }
var rating: Int { get set }
}
protocol RateableItemManager {
/// get some objects matching query criteria
func objects(matching query: RateableItemQuery) -> [RateableItem]
/// get a specific object
func object(withID identifier: String) -> RateableItem?
/// persists them
func save(_ object: RateableItem) throws
/// deletes the objects.
func delete(_ objects: [RateableItem])
/// creates a new object.
func create() -> RateableItem
}
and
struct RateableItemQuery {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((RateableItem, RateableItem) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((RateableItem, RateableItem) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
I can now implement a concrete type of this which returns concrete types that conform to the protocol. The concrete types that are returned are irrelevant to the rest of my code because the rest of the code only cares that they conform to a protocol. This enables me to make 'production' and 'dummy' versions of the models.
Is there a way where I can define this more generally, such as:
struct Query<T> {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((T, T) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((T, T) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
such that
struct RateableItemQuery: Query<RateableItem> {}
and
protocol ItemManager<T> {
func objects(matching query: Query<T>) -> [T]
func object(withID identifier: String) -> T?
func save(_ object: T) throws
func delete(_ objects: [T])
func create() -> T
}
and
protocol RateableItemManager: ItemManager<RateableItem>
Because I want to use this API paradigm, but don't necessarily want to constrain anything at the 'base protocol' level, as I would often just be re-writing these method signatures for the various Protocol-types I'd want to work with.
If I'm not mistaken, associated types have to be concrete, making their return types also concrete, and then I can't easily work with protocol types.
Sorry if I didn't speak "canonically". I hope I was able to convey my intentions.
Could this be what upcoming Swift 5.1 is offering in terms of opaque types, returning -> some ProtocolType ?
So it turns out Opaque types aren't the answer either.
I solved my problem by making abstract baseclasses for Managers, the Query and QueryResults were generic structs, and the concrete subclasses for the Managers could take and return protocol-based Data types.
public struct Query<T> {
var searchPredicate: NSPredicate?
var sortingBlock: ((T, T) throws -> Bool)?
var groupingSpecifier: (() -> String)?
var results: QueryResults<T>?
init(matching predicate: NSPredicate?,
sort: ((T, T) throws -> Bool)?,
groupBy: (() -> String)?) {
}
}
public struct QueryResults<T> {
public enum ChangeType: UInt {
case insert = 1
case delete
case move
case update
}
public struct Section<T> {
var items: [T]
var title: String?
}
public var sections: [Section<T>] = []
public func object(at indexPath: IndexPath) -> T? {
return nil
}
}
public class AnyObjectManager<ObjectType> {
public enum Error: Swift.Error {
case abstractImplementationRequiresOverride
}
typealias QueryDidChangeObjectBlock = ((
_ query: Query<ObjectType>,
_ didChangeObject: ObjectType,
_ atPath: IndexPath?,
_ forChangeType: QueryResults<ObjectType>.ChangeType,
_ newIndexPath: IndexPath?) -> Void)
typealias QueryDidChangeSectionBlock = ((
_ query: Query<ObjectType>,
_ didChangeSection: QueryResults<ObjectType>.Section<ObjectType>,
_ atSectionIndex: Int,
_ forChangeType: QueryResults<ObjectType>.ChangeType) -> Void)
/// get some objects matching query criteria. nil means return all
func objects(matching query: Query<ObjectType>?) -> [ObjectType] {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// get a specific object
func object(withID identifier: String) -> ObjectType? {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// deletes the objects. Does it commit that to disk?
func remove(_ objects: [ObjectType]) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// creates a new object but does not save it.
func create() -> ObjectType {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// this is basically to mimic the functionality of a fetched results controller...
func monitorQuery(_ query: Query<ObjectType>,
willChangeBlock: ((_ query: Query<ObjectType>) -> Void)?,
didChangeObjectBlock: QueryDidChangeObjectBlock?,
didChangeSectionBlock: QueryDidChangeSectionBlock?,
didFinishChangesBlock:((_ query: Query<ObjectType>) -> Void)?) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// and this is to stop monitoring that.
func stopMonitoringQuery() {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func saveChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func discardChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
}

Is it possible to guarantee that the enums has a specific case?

I have a protocol:
protocol ReduxTransition {}
I have a enums:
enum ProfileTransition: ReduxTransition {
case authorization
...
case showNotification(NotificationType)
}
enum RatingTransition: ReduxTransition {
case pop
...
case showNotification(NotificationType)
}
And I want to do stuff like this to avoid using similar implementations for showing notifications
func processError(performTransition: #escaping (ReduxTransition) -> ()) {
var notification: NotificationType!
performTransition(.showNotification(notification))
}
If anyone is interested, I applied the following solution:
protocol NotificationPresentable {
static func getNotificationTransition(of type: NotificationType) -> Self
}
extension ProfileTransition: NotificationPresentable {
static func getNotificationTransition(of type: NotificationType) -> ProfileTransition {
return .showNotification(type)
}
}
extension RatingTransition: NotificationPresentable {
static func getNotificationTransition(of type: NotificationType) -> RatingTransition {
return .showNotification(type)
}
}
func processError<Transition: NotificationPresentable>(performTransition: #escaping (Transition) -> ()) {
let notification: NotificationType!
...
performTransition(Transition.getNotificationTransition(of: notification))
}
Maybe someone knows how to make this solution even more better?

Can I specialize a generic method implementation in Swift?

I would like to implement a generic function in two ways, one of them specialized to a specific generic value, but so far I was not able to express this in Swift 3.
My setup is like this:
protocol Convertible {
func convert<T>() -> Converted<T>
}
struct Unconverted: Convertible {
func convert<T>() -> Converted<T> {
return Converted<T>()
}
}
struct Converted<T>: Convertible {
func convert<T2>() -> Converted<T2> {
return Converted<T2>()
}
}
What I would like to achieve is to add a special case for convert() on Converted<T>, for the case where T2 == T, so the following behavior is achieved:
let unconverted: Convertible = Unconverted()
let converted: Convertible = unconverted.convert() as Converted<String>
// double conversion to same generic type is ok, should ideally return self
let convertedOk = converted.convert() as Converted<String>
// re-conversion to another type is not ok and should fail
let convertedFail = converted.convert() as Converted<Int>
I tried the following approaches, but none of them worked out:
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
fatalError("already converted")
}
// error: Same-type requirement makes generic parameters 'T2' and 'T' equivalent
func convert<T2>() -> Converted<T2> where T2 == T {
return self
}
}
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
fatalError("already converted")
}
// method is never called, even when T2 == T
func convert() -> Converted<T> {
return self
}
}
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
// does not call the specialized method, even when T2 == T
self.internalConvert(type: T2.self)
}
private func internalConvert<T2>(type: T2.Type) -> Converted<T2> {
fatalError("already converted")
}
private func internalConvert(type: T.Type) -> Converted<T> {
return self
}
}
Is there a way to express this?

How to create a closure to return which one of the parameters is true?

I need to create some sort of closure to return back if it's an optional or forced update. I've created some pseudo code:
func verifyAppVersionWithServer(isForceUpdate: bool -> true, isOptionalUpdate: bool -> true) {
//Some check will be performed here then:
if isForceUpdate {
return isForceUpdate -> true
} else {
return isOptionalUpdate -> true
}
}
I'm not sure how to create a closure in Swift which will then return which of the parameters is true.
It is probably nicer to return an enum that indicates the type of update required.
You would then have something like this:
enum UpdateType {
case None
case Optional
case Required
}
func verifyAppVersionWithServer(completion:(UpdateType) -> Void) {
let anyUpdate = true
let forcedUpdate = false
if anyUpdate {
if forcedUpdate {
completion(.Required)
} else {
completion(.Optional)
}
} else {
completion(.None)
}
}
You would call it as:
verifyAppVersionWithServer { (updateType) in
print("Update type is \(updateType)")
}
Obviously the values would be determined by your server response, not fixed values as I have shown.
You can use something like below
func verifyAppVersionWithServer(parm1: String, withParma2: Bool, completionHandeler: (isSucess: Bool, error : NSError) -> Void) {
//Write your logic
//call complition handeler
completionHandeler(isSucess: true, error: error)
}
Hope this will help

Extending SignalProducerType in case Value is an Array<SomeProtocol>

I have a protocol for fetching database objects by PrimaryKey
typealias PrimaryKey = String
protocol PrimaryKeyConvertible {
var pkValue : PrimaryKey { get }
static func pkObject(key: PrimaryKey) -> Self?
}
and I want to extend the SignalProducerType to be able to operate on a SignalProducer.Value of that type.
So the Single object extension (single as in not Array) works fine and implemented as following:
extension SignalProducerType
where Value: PrimaryKeyConvertible
{
func fetchOnMainThread() -> SignalProducer<Value?, Error> {
return
self.map{ (obj: Value) -> PrimaryKey in
return obj.pkValue
}
.observeOn(UIScheduler())
.map{ (key: PrimaryKey) -> Value? in
return Value.pkObject(key)
}
}
}
But when I try to implement it on an Array of these elements i hit some compilation challenges:
extension SignalProducerType
{
func fetchOnMainThread<P: PrimaryKeyConvertible where Self.Value == Array<P>>() -> SignalProducer<[P], Error> { //(1)
return self.map({ (value: Self.Value) -> [PrimaryKey] in
return value.map{ $0.pkValue } //(2)
})
}
}
(1) i suspect that the signature is not communicating the idea to the compiler correctly
(2) produces the following error:
Type of expression is ambiguous without more context
the issue i'm trying to solve is how to let the compiler recognize the the SignalProducer is operating on an Array<P> where P is PrimaryKeyConvertible and have the .map operate on it accordingly ...
my current solution for the array issue is to implement using a generic function as listed below:
func fetchOnMainThread<Value: PrimaryKeyConvertible, Error: ErrorType>
(signal: SignalProducer<[Value], Error>) -> SignalProducer<[Value], Error> {
return signal
.map{ (convertibles: [Value]) -> [PrimaryKey] in
return convertibles.map { $0.pkValue }
}
.observeOn(UIScheduler())
.map{ (keys: [PrimaryKey]) -> [Value] in
return keys.flatMap{ Value.pkObject($0) }
}
}
and then used for example:
extension GoogleContact: PrimaryKeyConvertible {...}
extension GoogleContact {
static func fetchGoogleContactsSignal() -> SignalProducer<[GoogleContact], GoogleContactError> { ...}
}
and the call site would be like:
let signal = fetchOnMainThread(GoogleContacts.fetchGoogleContactsSignal()).onNext...
where I would prefer to have it as an extension where it would flow as usual
GoogleContacts
.fetchGoogleContactsSignal()
.fetchOnMainThread()
Update
another version of the function I've tried : (#J.Wang)
extension SignalProducerType
where Value == [PrimaryKeyConvertible]
{
func fetchArrayOnMainThread2<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
return self
.map{ (values: Self.Value) -> [PrimaryKey] in
return values.map{ $0.pkValue }
}
.deliverOnMainThread()
.map{ (keys: [PrimaryKey]) -> [T] in
return keys.flatMap{ T.pkObject($0) }
}
}
}
let signal =
GoogleContacts
.fetchGoogleContactsSignal()
.fetchArrayOnMainThread2() //(3)
(3) Generates error:
'[PrimaryKeyConvertible]' is not convertible to '[GoogleContact]'
Hmm, although I'm not quite sure what the problem is, but I think the following implementation might be what you want.
extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
func fetchOnMainThread() -> SignalProducer<[PrimaryKey], Error> {
return self.map { value in
value.map { $0.pkValue }
}
}
}
Try this:
extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
func fetchOnMainThread<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
return self.map { value in
value.map { $0.pkValue }
}.map { keys in
keys.flatMap { T.pkObject($0) }
}
}
}