Please don't close because of duplicate of "Ambiguous use of...". Even with intense research I was not able to find any similar threads solving my issue.
I'm updating a project to Swift 3 and am stuck at a compiler error:
Ambiguous use of 'authorize(_:completion:)'
Code:
func connectToInstagram() {
let auth: NSMutableDictionary = ["client_id": INSTAGRAM_CLIENT_ID,
SimpleAuthRedirectURIKey: INSTAGRAM_REDIRECT_URI]
SimpleAuth.configuration()["instagram"] = auth
SimpleAuth.authorize("instagram") { (anyObject, error) in // error here
if anyObject != nil {...
SimpleAuth is a Framework to handle Social Media authentication written in Objective C.
SimpleAuth:
open class SimpleAuth : NSObject {
open class func authorize(_ provider: String!, completion: SimpleAuth.SimpleAuthRequestHandler!)
SimpleAuthRequestHandler:
public typealias SimpleAuthRequestHandler = (Any?, Error?) -> Swift.Void
public let SimpleAuthPresentInterfaceBlockKey: String
public let SimpleAuthDismissInterfaceBlockKey: String
I have tried to change the line to:
_ = SimpleAuth.authorize("instagram") { (anyObject: Any?, error: Error?) in
_ = SimpleAuth.authorize("instagram", completion: { (anyObject: Any?, error: Error?) in
But just as expected, it didn't changed anything. What am I missing? Help is very appreciated.
Build Log:
xy/InstagramVC.swift:409:9: error: ambiguous use of 'authorize(_:completion:)'
SimpleAuth.authorize("instagram") { (any: Any?, error: Error?) -> Swift.Void in
^
SimpleAuth.SimpleAuth:24:21: note: found this candidate
open class func authorize(_ provider: String!, completion: SimpleAuth.SimpleAuthRequestHandler!)
^
SimpleAuth.SimpleAuth:34:21: note: found this candidate
open class func authorize(_ provider: String!, options: [AnyHashable : Any]! = [:], completion: SimpleAuth.SimpleAuthRequestHandler!)
The problem is with the API. They provide these two functions:
open class func authorize(_ provider: String!, completion: SimpleAuth.SimpleAuthRequestHandler!)
and
open class func authorize(_ provider: String!, options: [AnyHashable : Any]! = [:], completion: SimpleAuth.SimpleAuthRequestHandler!)
but as you can see the options argument of the second function has been given a default argument ([:]). So when you don't specify the options parameter, the compiler can't tell which function you want to call.
This means the first function can't be used. You'll always have to use the second function and pass an explicit options argument. So:
authorize("instagram", options: [:]) { (any: Any?, error: Error?) -> Void in ... }
It's probably worth filing a bug with the SimpleAuth framework author(s).
Related
I'm new to Swift, after many years as an Objective-C developer. I'm struggling to understand how type casting works with generics.
I have two functions that are doing object mapping with Alamofire + Codable and Alamofire + ObjectMapper. Something like this:
public func performRestOperationWithDecodable<ResponseClass: Decodable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: #escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock:#escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseDecodableObject(
queue: self.dispatchQueue,
keyPath: nil,
decoder: JSONDecoder(),
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
public func performRestOperationWithObjectMapper<ResponseClass: BaseMappable>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters,
encoding: ParameterEncoding,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: #escaping (_ responseObject: DataResponse<ResponseClass>) -> Void,
errorBlock: #escaping (_ error: Error, _ validResponse: Bool) -> Void) {
let restConfiguration = self.setupRestOperation(
savedAuthType: savedAuthType,
pathUrl: pathUrl,
activityIndicator: activityIndicator)
Alamofire
.request(
restConfiguration.url,
method: .get,
parameters: parameters,
encoding: encoding,
headers: restConfiguration.headers)
.validate(
contentType: Constants.Network.DefaultValidContentTypeArray)
.responseObject(
queue: self.dispatchQueue,
keyPath: nil,
mapToObject: nil,
context: nil,
completionHandler: { (responseObject: DataResponse<ResponseClass>) in
self.completionRestOperation(
responseObject: responseObject,
activityIndicator: activityIndicator,
successBlock: successBlock,
errorBlock: errorBlock)
})
}
Each function has a generic type that conform to appropriate protocol needed by the mapper used (Decodable for Codable, Mappable for ObjectMapper). And these funcion compliles and worked as expected.
Now I'm trying to write a third function that have a third Generics type, but without conforming to any protocol, and forcing cast to the appropriate generics depending on a configuration parameter. Something like that:
public func performRestOperation<ResponseMappedClass :AnyObject>(
_ restOperationType: NetworkServiceRestOperationType,
pathUrl: URLConvertible,
parameters: Parameters = [:],
encoding: ParameterEncoding = URLEncoding.queryString,
savedAuthType: NetworkServiceAuthType,
activityIndicator: ActivityIndicatorProtocol?,
successBlock: #escaping (_ responseObject: ResponseMappedClass) -> Void,
errorBlock: #escaping (_ error: Error, _ validResponse: Bool) -> Void) {
if self.canDoRestOperation(errorBlock: errorBlock) != true {
return
}
switch self.mappingType {
case .Codable:
self.performRestOperationWithDecodable(
restOperationType,
pathUrl: pathUrl,
parameters: parameters,
encoding: encoding,
savedAuthType: savedAuthType,
activityIndicator: activityIndicator,
successBlock: { (responseObject: DataResponse<ResponseMappedClass>) in
let response: ResponseMappedClass = responseObject.result.value!
successBlock(response)
} as! (DataResponse<ResponseMappedClass & Decodable>) -> Void,
errorBlock: { (error: Error, validResponse: Bool) in
errorBlock(error, validResponse)
})
case .ObjectMapper:
// TODO
break
}
}
With the instruction: as! (DataResponse<ResponseMappedClass & Decodable>) -> Void, I'm trying to cast from the "generic" class type ResponseMappedClass to the same class, but with Codable support.
But that instruction doesn't compile:
Non-protocol, non-class type 'ResponseMappedClass' cannot be used
within a protocol-constrained type
After all that process, the various generics will represent the same class, for example SomeModelObject, that implements Codable, Mappable or maybe something else in future, so the usual type substitution at compile time, have to work anyway.
Any suggestions? Is it totally impossible to do in Swift?
I don't think it is possible to use a generic types in combination with protocols. The compiler doesn't consider them as class or protocol type. One more thing is wrong with your code, you are using self.mappingType to decide is it a Decodable or Mapable type. What if self.mappingType == .Codable, but ResponseMappedClass doesn't confirm to Decodable protocol? What I can suggest to you is function overloading. Create 3 functions:
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: Decodable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) where ResponseMappedClass: BaseMappable {
}
public func performRestOperation<ResponseMappedClass: AnyObject>(...) {
}
in the last one just throw an error. The right funcion will be called depending on the generic parameter type.
(DataResponse<ResponseMappedClass & Decodable>)
What I think you are doing wrong is that ResponseMappedClassis a class and Decodable on the other hand is a protocol. And you can not cast a class & protocol type together.
So, the error is exactly as it says.
Non-protocol, non-class type 'ResponseMappedClass' cannot be used within a protocol-constrained type
Something like this will work as both the types are protocol.
as! (DataResponse<Codable & Decodable>) -> Void
I have a static function that uses generics, but I can't get it to infer the generic type when it's called. The function:
static func getDocument<T: JSONDecodable>(_ document: String, fromCollection collection: FirebaseStorage.FirestoreCollections, completion: #escaping (_ decodedDoc: T?, _ error: Error?) -> ()) {
let docRef = firestore.collection(collection.rawValue).document(document)
docRef.getDocument { documentSnapshot, error in
guard error == nil,
let docData = documentSnapshot?.data(),
let decodedDoc = T(json: docData) else {
completion(nil, error)
return
}
completion(decodedDoc, nil)
}
}
Called using:
FirebaseClient.getDocument(
id,
fromCollection: FirebaseStorage.FirestoreCollections.users) { (profile, error) in
}
This gives the error: Generic parameter 'T' could not be inferred. How can I make the generic part of the function work?
FirebaseClient.getDocument(
id,
fromCollection: FirebaseStorage.FirestoreCollections.users) { (profile: ProfileType?, error) in
}
You'll need to let Swift know what type profile is where I've added ProfileType. That should do it!
Kane's answer is good, but a more flexible approach is to pass the type directly. For example, this makes it possible to have an optional completion handler, or to ignore the parameter with _ if you don't care about it. (That said, this approach is a little longer to type, so sometimes Kane's way is better.)
static func getDocument<T: JSONDecodable>(_ document: String,
ofType: T.Type,
completion: #escaping (_ decodedDoc: T?, _ error: Error?) -> ())
This makes everything explicit. You call it this way:
FirebaseClient.getDocument(id, ofType: ProfileType.self) { (profile, error) in ... }
Note that there's no need to use the ofType parameter for anything. It's just there to specialize the generic.
This is pretty close to how Decodable works, and is applicable to a lot of problems. But Kane's solution is also handy at times if it's more convenient.
I'm trying to invoke this function, where DataObject is a protocol that User uses:
static func makePostRequest<T:DataObject>(to endpoint:String, dataObject: T, objectType: T.Type, completionHandler: #escaping (DataObject?, Error?) -> Void) {
//some code
}
But I can't invoke it with this parameters:
static func create<T : DataObject>(_ object: T, completionHandler: #escaping (DataObject?, Error?) -> Void) {
let endpoint = NetworkManager.shared.baseURL + UserDAO.methodPath
NetworkManager.makePostRequest(to: endpoint, dataObject: object, objectType: User.self, completionHandler: completionHandler)
}
An error appears to me saying I can't invoke it with this list of parameters, but all of them are correct... I really don't know what's happening.
Error:
Cannot invoke 'makePostRequest' with an argument list of type '(to: String, dataObject: T, objectType: User.Type, completionHandler: (DataObject?, Error?) -> Void)'
I guess you need to call it like this, at least it worked for me in playground.
makePostRequest(to: endpoint, dataObject: object, objectType: T.self, completionHandler: completionHandler)
or add other generic variable
func makePostRequest<T: DataObject, U>(to endpoint:String, dataObject: T, objectType: U.Type, completionHandler: #escaping (DataObject?, Error?) -> Void)
I do not know what are you trying to achieve, so this might not help you at all
I see this error in the Analyze output for an app we are taking over:
/Users/bonnie/dev/elappa/Classes/FLAppGateway.swift:27:40: Forced cast from 'Error?' to 'NSError' only unwraps and bridges; did you mean to use '!' with 'as'?
The associated code block is like:
func fetchCountryCodes(_ completion: GetCountryCodesCompletion?)
{
let parameters = parametersToFetchCountryCodes()
serverManager?.postRequest(toURN: kGetCountryCallingCodes,
parameters: parameters,
domain: kUBPublicContentDomain,
success: { (object) in
self.handleGetCountryCodesSuccess(forObject: object as AnyObject?, completion: completion)
}) { (object, error) in
completion?(.failure(error as! NSError))
}
}
I am new to Swift. I read about Optionals and understand this is some issue with doing a forced class cast (using "as!") but not sure what. Any ideas?
elsewhere I see this code:
typealias GetCountryCodesCompletion = (Result<CountryCodes?, NSError>) -> ()
protocol AppGateway
{
func fetchCountryCodes(_ completion: GetCountryCodesCompletion?)
func marketingContents(completion: ((Result<[MarketingContent], NSError>) -> ())?)
}
Any ideas how to fix this Analyze/compiler warning?
This error happens because you are force casting using as! from a swift style Error to objective-c style NSError and this cannot be done.
So if you really need NSError, simple create one from your Error object by filling in the follow code
let objcError = NSError(domain: "somedomain", code: 123, userInfo: [:])
And then
completion?(.failure(objcError))
I am trying to get authorisation to use the addressbook using the CNContactStore function requestAccessForEntityType but I get an error I don't understand.
The function is defined in the class as:
public func requestAccessForEntityType(entityType: CNEntityType, completionHandler: (Bool, NSError?) -> Void)
This is my code:
let addressBookRef: CNContactStore = CNContactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: authorizationHandler)
func authorizationHandler(granted: Bool, error: NSError?) {
}
Compiler error:
Extra argument 'completionHandler' in call
Turns out that I was defining the property directly in the class. Obviously you can't run a function there and so it was not working. Duh!
All I need to do was put in the class:
let addressBookRef = CNContactStore()
And the following when it was time to actually ask for permission:
addressBookRef.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: authorizationHandler)