I have two questions involving Generics
a) I want to do an extension function where my Result class in the case of Success accepts a Generic of type T, however, I get the following error: Use of undeclared type 'T'
Here's my code:
extension Result<T> where Success == [T] {
/// Function extension for the .success case on a Result object. The result of the Result object are
/// multiple object, in case a list or an iterable in being wrapped.
/// - Parameter callback: Function that the will be executed when the .success case is triggered
func onSuccess(callback: ([T]) -> Void) {
switch self {
case .success(let results):
callback(results)
default:
return
}
}
}
also regarding the same problem, I want a UIViewController to accept an array of a Generic type:
:
class UIObservableTableViewController: UITableViewController, LifecycleOwner {
func addLiveData<T>(_ liveData: LiveData<T>) {
liveDataObservers.append(liveData)
}
private lazy var liveDataObservers = [LiveData<T>]()
However, I get the same error as the previous one, how can I implement T instead of Any, or could I cast T to Any or the other way around?
I have also tried the following code:
extension Result where Success == Collection {
/// Function extension for the .success case on a Result object. The result of the Result object are
/// multiple object, in case a list or an iterable in being wrapped.
/// - Parameter callback: Function that the will be executed when the .success case is triggered
func onSuccess(callback: (Success) -> Void) {
switch self {
case .success(let results):
callback(results)
default:
return
}
}
}
However, I get the following compilation error: Protocol 'Collection' can only be used as a generic constraint because it has Self or associated type requirements
LiveData is the following class:
/// Class for implementing the Observable Pattern. This is a Mutable Observable (it can change its value)
class LiveData<T> {
private (set) var value: T
private var valueChanged: ((T) -> Void)?
init(value: T) {
self.value = value
}
/// Function that sets a value on a main thread. This could throw an error is the setValue is not called inside the Main Thread
/// - Parameter value: T object
fileprivate func setValue(value: T) {
print("The value is of type: \(value)")
try! assertMainThread(methodName: "setValue")
self.value = value
valueChanged?(self.value)
}
/// Function that sets a value on a background thread. This could throw an error is the setValue is not called inside the Main Thread
/// - Parameter value: T object
fileprivate func postValue(value: T) {
DispatchQueue.main.async {
self.value = value
self.valueChanged?(self.value)
}
}
/// Function that checks if the current thread is the MainThread
private func assertMainThread(methodName: String) throws {
if !Thread.isMainThread {
throw CustomError("\(methodName) is not in the main thread")
}
}
/// Add closure as an observer and trigger the closure imeediately if fireNow = true
func observe(owner: LifecycleOwner, _ onChange: ((T) -> Void)?) {
valueChanged = onChange
onChange?(value)
// Add the live data into its LifecycleOwner
owner.addLiveData(self)
}
func removeObserver() {
valueChanged = nil
}
}
/// Class for implementing the Observable Pattern. This is a Mutable Observable (it can change its value)
class MutableLiveData<T>: LiveData<T> {
override func setValue(value: T) {
super.setValue(value: value)
}
override init(value: T) {
super.init(value: value)
}
override func postValue(value: T) {
super.postValue(value: value)
}
}
Related
I have managed to create an APIClient and router for my app and here is my code
my result
enum Result<T> {
case success(data: T)
case failure(error: Error) }
Here is my server request
func request<T: Decodable>(router: APIRouter, completion: #escaping (Result<T>) -> ()) {
And the completion handler
completion(Result<T>.failure(error: ErrorResult.defaultError))
completion(Result.success(data: result))
And here is my viewModel where i am getting the result
switch result {
case .success(let successData):
print("successData \(successData)")
self.viewModelDelegate?.didFinishFetchingData(successData)
case .failure(let errorData):
print("errorData \(errorData.localizedDescription)")
self.viewModelDelegate?.didFinishFetchingDataWithFailure?(errorData.localizedDescription)
}
Now what i need is i want to pass the result as decodable to my viewcontroller using delegate
#objc public protocol ViewModelDelegate {
func didStartFetchingData()
func didFinishFetchingData(_ result:Decodable)
#objc optional func didFinishFetchingDataWithFailure(_ message:String)
}
This is what I did but it is getting error
All I need is I want to pass the success data (decodable data) to my view controller from view model using custom delegate
You can do generic with this, check out the below example
func didFinishFetchingData<T: Decodable>(_ result: T)
// example models
struct Foo: Decodable {
}
struct Boo: Decodable {
}
didFinishFetchingData(Foo())
didFinishFetchingData(Boo())
To make it optional as you need , simply add ? in T .
func didFinishFetchingData<T: Decodable>(_ result: T?)
Note : You can't pass plain nil into the parameter but you can pass a nullable type of T check the code below .
let model = try? JSONDecoder().decode(Foo.self, from: Data()) // model is type of Foo?
didFinishFetchingData(model) // works fine
There's a crash in parameter casting:
Could not cast value of type NSStackBlock (0x1030b1e78) to '(__C.UNNotificationPresentationOptions) -> ()' (0x1030b0208).
2018-02-19 17:40:31.204021+0200 [2407:480530]
Could not cast value of type NSStackBlock (0x1030b1e78) to '(C.UNNotificationPresentationOptions) -> ()' (0x1030b0208).
extension Reactive where Base: UNUserNotificationCenter {
public var delegate: DelegateProxy<UNUserNotificationCenter, UNUserNotificationCenterDelegate> {
return RxNotificationServiceDelegateProxy.proxy(for: base)
}
var didReceiveResponce: Observable<UNNotificationResponse> {
return delegate.methodInvoked(#selector(UNUserNotificationCenterDelegate.userNotificationCenter(_:didReceive:withCompletionHandler:))).map { parameters in
return parameters[1] as! UNNotificationResponse
}
}
var willPresentNotification: Observable<((UNNotificationPresentationOptions) -> Void)> {
return delegate.methodInvoked(#selector(UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:withCompletionHandler:))).map { parameters in
return parameters[2] as! ((UNNotificationPresentationOptions) -> Void) **CRASH!!!**
}
}
}
All I need is to show localNotifications in the foreground, so delegate method needed accordingly, but can not figure out how to do so I can pass the closure as a parameter to an Observable or if it's not possible or not needed I will be glad to read about your way of solving such issue in RxSwift manner.
I am experimenting with generics in Swift and I am attempting to push it to its limits.
In my application I have a super simple API wrapper around Alamofire. The structure is like so:
API -> Request -> Alamofire request
Here is some generic code that I threw into a playground to test some concepts. Here is what I have so far:
protocol SomeProtocol {
var cheese: String { get }
init()
}
class Something: SomeProtocol {
required init() { }
var cheese: String {
return "wiz"
}
}
class API {
class func performRequest<T: SomeProtocol>(completion: (T?, NSError) -> Void) {
// This code is irrelevant, just satisfying the completion param
let test = T()
let error = NSError(domain: "Pizza", code: 1, userInfo: nil)
completion(test, error)
}
}
func test() {
API.performRequest<Something> { item, error in
}
}
Calling the function gives the error:
"Cannot explicitly specialize a generic function"
****** UPDATE ******
As per the answer below, removing the typical <> generic type specifier and instead adding the expected type to the completion params solves the issue. Just a quick example:
func test() {
API.performRequest { (item: Something?, error) in
}
}
Additionally, I have discovered that making the API wrapper class a generic class solves the issue like so:
protocol SomeProtocol {
var pizza: String { get }
}
class SomeObject: SomeProtocol {
var pizza: String { return "pie" }
}
class API<T: SomeProtocol> {
class func performRequest(completion: (T?, NSError?) -> Void) {
}
}
func test() {
API<SomeObject>.performRequest { item, error in
// Do something with item, which has a type of SomeObject
}
}
Either way, the end goal is accomplished. We have a single generic method that will perform a set of tasks and return, via completion closure, the object based on the type passed in with each use.
The way generics work is they allow a function to use unspecialized variables inside of its implementation. One can add functionality to these variables by specifying that the variables must conform to a given protocol (this is done within the declaration). The result is a function that can be used as a template for many types. However, when the function is called in the code itself, the compiler must be able to specialize and apply types to the generics.
In your code above, try replacing
func test() {
API.performRequest<Something> { item, error in
}
}
with
func test() {
API.performRequest { (item: Something?, error) in
}
}
this lets the compiler know which type it must apply to the function without explicitly specifying. The error message you received should now make more sense.
Here is what i did using alamofire and alamofire object mapper:
Step 1: Create modal classes that conforms to Mappable protocols.
class StoreListingModal: Mappable {
var store: [StoreModal]?
var status: String?
required init?(_ map: Map){
}
func mapping(map: Map) {
store <- map["result"]
status <- map["status"]
}
}
Step 2: Create a fetch request using the generic types:
func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) {
Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in
guard response.result.isSuccess else{
print("Error while fetching: \(response.result.error)")
completion(nil, response.result.error)
return
}
if let responseObject = response.result.value{
print(responseObject)
completion(responseObject, nil)
}
}
}
Step 3: Now all you need is to call this fetch function. This can be done like this:
self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in
}
You will not only get your response object but it will also be mapped to your modal class.
Is it possible to return a type of generic that conforms to protocol for later use with class functions using Swift 1.2? Take a look:
protocol SomeProtocol
{
static func start(kind: Kind)
}
class A: SomeProtocol
{
class func start(kind: Kind)
{
print("A started")
}
}
class B: SomeProtocol
{
class func start(kind: Kind)
{
print("B started")
}
}
enum Kind {
case Akind
case Bkind
private func classKind<T: SomeProtocol>() -> T.Type
{
switch self {
case .Akind: return A.self
case .Bkind: return B.self
}
}
func doSomething() {
self.classKind().start(self)
}
}
I tried various methods but every of them ended with some errors. Currently I got 'A' is not a subtype of 'T' in classKind method (same for 'B') and cannot invoke 'start' with an argument list of type '(Kind)' in doSomething.
I'm sure I'm pretty close but can't solve it...
If you're using Swift 2, to achieve what you want you only need to change:
private func classKind<T: SomeProtocol>() -> T.Type { ... }
to
private func classKind() -> SomeProtocol.Type { ... }
Now back to the not-working code to see where the errors were coming from. You don't need to make the changes I'm now detailing, this is just to explain the errors.
First examine your doSomething method:
func doSomething() {
self.classKind().start(self)
// Error: Argument for generic parameter 'T' could not be inferred.
//
// (I'm using Xcode 7 b6, which may explain the differing error messages)
}
For the type returned by classKind to be inferred, you'd have to do:
let type: A.Type = self.classKind() // Or you could use `B.Type`.
type.start(self)
Which obviously defeats the point of your goal, since you have to specify the type you want.
Secondly, the errors in classKind:
private func classKind<T: SomeProtocol>() -> T.Type
{
switch self {
case .Akind: return A.self
// Cannot convert return expression of type 'A.Type' to return type 'T.Type'.
case .Bkind: return B.self
// Cannot convert return expression of type 'B.Type' to return type 'T.Type'.
}
}
To see why this doesn't work consider the following example, in which I have another type that conforms to SomeProtocol:
struct C: SomeProtocol { ... }
Then in doSomething:
func doSomething() {
let type: C.Type = self.classKind()
type.start(self)
}
The errors you're getting can now be read as: Cannot convert return expression of type 'A.Type'/'B.Type' to return type 'C.Type'.
Error: Cannot convert the expression type (String, MyType) to ()
From the following code
Test(method: {[weak self] (message: String) in self?.callback(message)}, instance: self)
and if I add a return statement, it works, and the error goes away
Test(method: {[weak self] (message: String) in self?.callback(message); return}, instance: self)
Not sure how to handle the above without having to have the dummy return statement, any advise.
Here's my class Test
public class Test {
private var instance: AnyObject?
private var method: ((message: String) -> ())?
public init(method: (String -> ())?, instance: AnyObject) {
}
}
Edit
I've done a playground based minimalistic example (please copy paste for a test)
class Test {
private var _method: ((String) -> ())?
weak private var _instance: AnyObject?
init(method: (String -> ())?, instance: AnyObject?) {
_method = method
_instance = instance
}
}
class Another {
func register() {
//this doesn't need a return
Test(method: {(message: String) in self.callback(message)}, instance: self)
//this needs a return once I add [weak self]
Test(method: { [weak self] (message: String) in self?.callback(message); return}, instance: self)
}
func callback(message: String) {
println(message)
}
}
Not sure how to handle the above without having to have the dummy return statement, any advise.
You have solved the problem beautifully. Anonymous functions automatically use a one-line function body as a return value, so to prevent that from causing a type mismatch with the expected return type (Void) you have to add another line of code so that it is not a one-line function body. The dummy return statement, which itself returns Void, is a great way to handle it; I would just use that and move on. There are some snazzier workarounds but what you have is precisely what I would do.
EDIT: To understand the source of the type mismatch, try this:
struct Test {
func voider() -> Void {}
}
let testMaybe = Optional(Test())
let result = testMaybe?.voider()
Now result is not a Void; it's an Optional wrapping a Void. That is what's happening to you; a Void is expected but your one-line anonymous function returns an Optional wrapping a Void. By adding another line that returns Void explicitly, you solved the problem.
The implicit return is returning the result of your callback() method. That return value conflicts with the closure's return value of void. You thus need an explicit, if ugly, return.