I have a function like this:
func register<T: Routable>(_ action: Action, withCallback callback: #escaping (T?) -> Void) {
notificationCenter.addObserver(forName: actionMap[action], object: nil, queue: .main, using: { notification in
let routable = T(userInfo: notification.userInfo)
callback(routable)
})
}
Where Routable is defined like:
protocol Routable {
init?(userInfo: [AnyHashable: Any]?)
}
When I try to use it, I receive
Cannot explicitly specialize a generic function
This is the usage:
controller.register<Navigate>(Action.navigate) { [unowned self] navigate in
// do something
}
Any idea to make the compiler happy?
I believe this is purely a syntax issue. You can't pass the type parameter directly like this. You need to "fill in the type hole" instead. To do that, you need to add the type to navigate:
controller.register(Action.navigate) { [unowned self] (navigate: Navigate?) in ... }
Sometimes that syntax is annoying because it buries the type. You can improve it by rewriting the signature of register this way:
func register<T: Routable>(action: Action, returning: T.type,
withCallback callback: #escaping (T?) -> Void)
You'd then call it this way:
controller.register(action: .navigate, returning: Navigate.self) {
[unowned self] navigate in
// ...
}
The returning parameter isn't directly used in the function. It just provides a more explicit way to specialize the function.
It's really hard to say without seeing a more complete code example... but basically what the compiler is telling you is "You told me you want the register function to be generic (because it has a type parameter) but then you are also trying to tell me exactly what type to use and I don't like that".
The point at which you are "explicitly specializ[ing]" the function is when you add a specific type parameter in the call:
controller.register<Navigate>...
# Right Here ^^^^^^^^^^
The compiler wants the flexibility to determine what type of register function to call. I suspect what you want is:
controller.register(Action.navigate) {...
Where you do not explicitly specialize the generic function but let the complier figure out which specialization to use.
Related
I had a function
func login<Bool> (parameters: [(String, Any)],
completion: #escaping (Bool) -> Void) {
//Do something
}
And whenever I called the completion handler like so
completion(false)
or
completion(true)
XCode kept telling me: "Cannot convert value of type 'Swift.Bool' to expected argument type 'Bool'"
Eventually, I removed the at the beginning of the function, and the error went away. I thought that was declaring the type for the function, but I'm frankly not sure what that means, or what <> does despite googling. I'm sure it's explained well, but I'm not grasping the concept from googling alone. And why is Swift.Bool not able to be converted to Bool. I was able to find that there is an objective C version of bool and I thought maybe it was asking for that, but that didn't work either. What does that mean a Bool is?
The syntax func login<Bool> defines a type parameter called Bool. It has nothing to do with Swift.Bool. It's just a name you've defined. It's the same as if you declared a local variable var Bool: String. You'd have a variable named Bool that is actually a String. Don't do that.
If you want the value to always be Bool, remove the type parameter. This isn't generic.
func login(parameters: [(String, Any)],
completion: #escaping (Bool) -> Void) {
For details on the <...> syntax and generics see Generics in The Swift Programming Language.
I've done some research on closures, closures that receive parameters, trailing closures, and completion handlers, but I'm having difficulty understanding when the two arguments to completion, manager and file are passed. In the pick() function definition, there is nowhere in the function scope that calls the completion with completion(manager, file) syntax. There is, however, a present method and I think I am missing something about present() that may include the call for the completion closure with proper arguments. I would appreciate your help.
public func pick(from vc: UIViewController?, withCompletion completion: #escaping (_ manager: HSDriveManager?, _ file: GTLRDrive_File?) -> Void) {
viewer?.completion = completion
viewer?.shouldSignInOnAppear = true
//As of now, present() seems to include the calling of the completion closure.
//self is the HSDriverPicker class
print(type(of: self))
vc?.present(self, animated: true)
}
The code below is the function call for pick. What confuses me is that in order for manager and file to act like arguments of the completion closure somewhere in the function call there has be a part that passes those arguments. I don't see them. I would appreciate your insight on what exactly the present method does and whether it takes care of calling the closure with necessary arguments.
picker.pick(from: self) {
(manager, file) in
print("picked file: \(file?.name ?? "-none-")")
let destinationPath = "/Users/james/Desktop/tests"
manager!.downloadFile(file!, toPath: destinationPath, withCompletionHandler: {
error in
if error != nil {
print("Error downloading : \(error?.localizedDescription ?? "")")
}
else {
print("Success downloading to : \(destinationPath)")
}
})
}
The function present(_:animated:) won't be responsible for calling the completion closure. viewer?.completion = completion means viewer will take care of calling the completion closure in its scope(that's why #escaping is used, i.e the closure will outlive the scope that you've passed it to). The two arguments manager and file aren't supposed to be passed by the client but are exposed to the client so that whoever calls the picker.pick could use these properties and perform the certain action that will then called inside viewer. The closure with parameters simply means the client could use that parameter without worrying about who and when will those parameters be passed.
Swift 4.2, Xcode 10.1
In the order processing app I'm working on, the user may do a search for orders already processed or submitted. When that happens, it will check to see if it has a cache of orders, and if it does not, it will refill that cache using an asynchronous API request, then check the cache again.
The function that refills the cache is a private static one that accepts an escaping completion handler. Whenever I have used that completion handler in the past, all I had to do was add a closure at the end of the function call. This was before I was instructed to make a cache of all data wherever possible, and only use the API to refill that cache. Since then, the function has become private, because there will never be a need to call the API directly from anywhere but within this class.
Now, when I put the closure directly after the function call, it's giving me an error that basically says I'm passing a #nonescaping closure instead of an #escaping closure:
"Cannot invoke 'getAndCacheAPIData' with an argument list of type '(type: Codable.Type, (String?) -> Void)', Expected an argument list of type '(type: CodableClass.Type, #escaping (String?) -> Void)'"
I've never had to explicitly declare a closure to be #escaping before, nether does it seem to be possible. I suspect that because the function is both private AND static, there's some kind of issue happening with the way closures are inferred to be #escaping. I'm out of my depth. I could try converting the static class to a singleton, but I'm hesitant to refactor a bunch of working code because of one error until I'm absolutely sure that change will resolve the issue, and that what I'm trying to do isn't possible unless I change my approach.
Here's the code:
public static func fillSearchResultArray<ManagedClass: NSManagedObject>(query:String, parameters:[String], with type: ManagedClass.Type, completionHandler: #escaping (String?)->Void)
{
let codableType:Codable.Type
switch type
{
case is ClientTable.Type:
codableType = ClientData.self
case is OrderTable.Type:
codableType = OrderData.self
case is ProductTable.Type:
codableType = ProductData.self
default:
completionHandler("Unrecognized type.")
return
}
let fetchedData:[ManagedClass]
do
{
fetchedData = try PersistenceManager.shared.fetch(ManagedClass.self)
}
catch
{
completionHandler(error.localizedDescription)
return
}
if fetchedData.isEmpty
{
AppNetwork.getAndCacheAPIData(type: codableType)//error here
{(firstErrorString) in
//move search array data to the cache
if firstErrorString.exists
{
completionHandler(error)
}
else
{
AppNetwork.fillSearchResultArray(query: query, parameters: parameters, type: type)
{ errorString in
completionHandler(errorString)
}
}
}
return
}
else
{ ...
The signature of the function being called:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
Why is swift inferring this closure to be the default #nonescaping when before it always inferred it to be #escaping?
The problem has nothing to do with the closure, or static, or private. It has to do with the type parameter. You cannot call this method:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
with a variable of type Codable.Type. The type value you pass must be a concrete type, known at compile-time. If you want to pass a variable, you can't use a generic. It would have to be:
private static func getAndCacheAPIData(type: Codable.Type, completionHandler: #escaping (String?)->Void)
Alternately, you can call this as:
AppNetwork.getAndCacheAPIData(type: Int.self) {(firstErrorString) in ... }
or some other known-at-compile-time type.
Probably what you really want here is something like:
let completion: (String?) -> Void = {(firstErrorString) in ... }
switch ... {
case ...:
AppNetwork.getAndCacheAPIData(type: Int.self, completion: completion)
case ...:
AppNetwork.getAndCacheAPIData(type: String.self, completion: completion)
...
The basic problem is that protocols do not conform to themselves, so a variable of type Codable.Type does not satisfy the : Codable requirement. This comes down to the same reason you can't just call:
AppNetwork.getAndCacheAPIData(type: Codable.self) {...}
Alternately, you could refactor it this way:
private static func handleAPI<CodableClass: Codable>(type: CodableClass.Type) {
getAndCacheAPIData(type: type.self) { _ in ... the completion handler ..}
}
switch ... {
case ...:
AppNetwork.handleAPI(type: Int.self)
case ...:
AppNetwork.handleAPI(type: String.self)
...
Side note: Any & is meaningless here. You just meant <CodableClass: Codable>.
I have the following function:
public enum RemoteRecordChange<T: ParsedProtocol> {
case insert(T)
case update(T)
case delete(RemoteRecordID)
}
func processRemoteChanges<T: ParsedProtocol>(_ changes: [RemoteRecordChange<T>], completion: #escaping () -> ()) {
// To Something
}
I am trying to call this function and pass in the parameter:
public struct Friend: ParsedProtocol {
// Has some properties
}
let friend = Friend()
// populate properties friend.username = "aUserName", etc.
When calling the function, I am getting an error:
self.processRemoteChanges([friend])
Compile Error:
`Cannot convert value of type '[Friend]` to expected argument type of `[RemoteRecordChange<..>]'
Not sure what I am doing wrong. I can provide more code, if needed.
What you want to do here does not make sense:
self.processRemoteChanges([friend])
The method processRemoteChanges` should take a list of "changes" as a parameter. What you are passing in is a list of friends. Does that make sense? No.
According to your enum, a change can be an insert, delete, or update. So choose one of them and pass it to processRemoteChange!
If you want to process a remote insert of the friend you just created:
// remember the completion handler!
processRemoteChanges([.insert(friend)], completion: {...})
If you want to process a remote update of a friend:
processRemoteChanges([.update(someFriend)], completion: {...})
If you want to process a remote delete of a friend:
processRemoteChanges([RemoteRecordChange<Friend>.delete(someID)], completion: {})
I am using Firebase to observe event and then setting an image inside completion handler
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
However I am getting this error
Closure cannot implicitly capture a mutating self parameter
I am not sure what this error is about and searching for solutions hasn't helped
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:) is most likely a value type (a struct?), in which case a mutating context may not explicitly capture self in an #escaping closure.
The simple solution is to update your owning type to a reference once (class).
The longer version
The observeSingleEvent(of:with:) method of Firebase is declared as follows
func observeSingleEvent(of eventType: FIRDataEventType,
with block: #escaping (FIRDataSnapshot) -> Void)
The block closure is marked with the #escaping parameter attribute, which means it may escape the body of its function, and even the lifetime of self (in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: #escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
SE-0035: Limiting inout capture to #noescape contexts
Stating [emphasis mine]:
Capturing an inout parameter, including self in a mutating
method, becomes an error in an escapable closure literal, unless the
capture is made explicit (and thereby immutable).
Now, this is a key point. For a value type (e.g. struct), which I believe is also the case for the type that owns the call to observeSingleEvent(...) in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).
The simplest solution to this issue would be making the type owning the observeSingleEvent(...) a reference type, e.g. a class, rather than a struct:
class Foo {
init() {}
private func bar(with block: #escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
Just beware that this will capture self by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly capture self weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
Sync Solution
If you need to mutate a value type (struct) in a closure, that may only work synchronously, but not for async calls, if you write it like this:
struct Banana {
var isPeeled = false
mutating func peel() {
var result = self
SomeService.synchronousClosure { foo in
result.isPeeled = foo.peelingSuccess
}
self = result
}
}
You cannot otherwise capture a "mutating self" with value types except by providing a mutable (hence var) copy.
Why not Async?
The reason this does not work in async contexts is: you can still mutate result without compiler error, but you cannot assign the mutated result back to self. Still, there'll be no error, but self will never change because the method (peel()) exits before the closure is even dispatched.
To circumvent this, you may try to change your code to change the async call to synchronous execution by waiting for it to finish. While technically possible, this probably defeats the purpose of the async API you're interacting with, and you'd be better off changing your approach.
Changing struct to class is a technically sound option, but doesn't address the real problem. In our example, now being a class Banana, its property can be changed asynchronously who-knows-when. That will cause trouble because it's hard to understand. You're better off writing an API handler outside the model itself and upon finished execution fetch and change the model object. Without more context, it is hard to give a fitting example. (I assume this is model code because self.img is mutated in the OP's code.)
Adding "async anti-corruption" objects may help
I'm thinking about something among the lines of this:
a BananaNetworkRequestHandler executes requests asynchronously and then reports the resulting BananaPeelingResult back to a BananaStore
The BananaStore then takes the appropriate Banana from its inside by looking for peelingResult.bananaID
Having found an object with banana.bananaID == peelingResult.bananaID, it then sets banana.isPeeled = peelingResult.isPeeled,
finally replacing the original object with the mutated instance.
You see, from the quest to find a simple fix it can become quite involved easily, especially if the necessary changes include changing the architecture of the app.
If someone is stumbling upon this page (from search) and you are defining a protocol / protocol extension, then it might help if you declare your protocol as class bound. Like this:
protocol MyProtocol: class {
...
}
You can try this! I hope to help you.
struct Mutating {
var name = "Sen Wang"
mutating func changeName(com : #escaping () -> Void) {
var muating = self {
didSet {
print("didSet")
self = muating
}
}
execute {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 15, execute: {
muating.name = "Wang Sen"
com()
})
}
}
func execute(with closure: #escaping () -> ()) { closure() }
}
var m = Mutating()
print(m.name) /// Sen Wang
m.changeName {
print(m.name) /// Wang Sen
}
Another solution is to explicitly capture self (since in my case, I was in a mutating function of a protocol extension so I couldn't easily specify that this was a reference type).
So instead of this:
functionWithClosure(completion: { _ in
self.property = newValue
})
I have this:
var closureSelf = self
functionWithClosure(completion: { _ in
closureSelf.property = newValue
})
Which seems to have silenced the warning.
Note this does not work for value types so if self is a value type you need to be using a reference type wrapper in order for this solution to work.