100

I am getting a 'Type of expression is ambiguous without more context ' on this part of code from a project I am trying to upgrade to latest Swift version. I can't seem to figure it out. I tried different things but can't get it to work.

The problem is on the syntax of this line

let imageToDeleteParameters  = imagesToDelete.map { ["id": $0.id, "url": $0.url.absoluteString, "_destroy": true] }

Full code:

extension TutorialCreationRequest: WebserviceParametrable {
    func toParameters() -> [String: AnyObject] {
        let imageParameters = images.map { ["url": $0] }
        let imageToDeleteParameters  = imagesToDelete.map { ["id": $0.id, "url": $0.url.absoluteString, "_destroy": true] }
        return [
            "title": title,
            "is_draft": isDraft,
            "difficulty": difficulty,
            "duration": duration,
            "cost": cost,
            "user_id": userId,
            "description": description,
            "to_sell": toSell,
            "images": [imageParameters, imageToDeleteParameters].flatMap { $0 }
        ]
    }
}
rmaddy
  • 298,130
  • 40
  • 468
  • 517
Florian VIDAL
  • 1,011
  • 2
  • 6
  • 6

14 Answers14

78

This happens when you have a function with wrong argument names.

Example:

functionWithArguments(argumentNameWrong: , argumentName2: )

and You declared your function as:

functionWithArguments(argumentName1: , argumentName2: ){}

This usually happens when you changed the name of a Variable. Make sure you refactor when you do that.

Daniel Bastidas
  • 1,365
  • 7
  • 14
46

This can happen if any part of your highlighted method or property is attempting to access a property or method with the incorrect type.

Here is a troubleshooting checklist, make sure:

  • the type of arguments match in the call site and implementation.
  • the argument names match in the call site and implementation.
  • the method name matches in the call site and implementation.
  • the returned value of a property or method matches in the usage and implementation (ie: enumerated())
  • you don't have a duplicated method with potentially ambiguous types such as with protocols or generics.
  • the compiler can infer the correct type when using type inference.

A Strategy

  • Try breaking apart your method into a greater number of simpler method/implementations.

For example, lets say you are running compactMap on an array of custom Types. In the closure you are passing to the compactMap method, you initialize and return another custom struct. When you get this error, it is difficult to tell which part of your code is offending.

  • For debugging purposes, you can use a for in loop instead of compactMap.
  • instead of passing the arguments, directly, you can assign them to constants in the for loop.

By this point, you may come to a realization, such as, instead of the property you thought you wanted to assign actually had a property on it that had the actual value you wanted to pass.

ScottyBlades
  • 7,536
  • 2
  • 50
  • 60
  • 5
    It is very unintuitive that Swift will report an error in *a completely unrelated part of the method* and not even hint in the error message that it could be in any part of the method. Furthermore, there is no hint as to where in the method the problem is in, forcing us to grasp blindly for a solution. At least knowing that the compiler can do this helps. – Andres Riofrio Aug 28 '20 at 07:30
  • 4
    The irony is "Error is ambiguous without more context." Just joking. I think the common thread is a type mismatch. If there is a type mismatch it takes two parts of your code to be out of sync. How is the compiler supposed to know which one you decide to be correct? That said, it could have some logic where it counts the consistent type usages, and then highlights the mismatched with the lowest count of common types. – ScottyBlades Aug 28 '20 at 07:37
  • 2
    Or highlight both and tell me what it knows about their types? Anything would be better than the completely opaque error message we currently get. – Andres Riofrio Aug 31 '20 at 08:04
  • https://medium.com/kinandcartacreated/contributing-to-swift-part-1-ea19108a2a54 – ScottyBlades Aug 31 '20 at 13:27
  • @AndresRiofrio, sounds good to me, let me know if you make a PR, I'd be happy to check it out. – ScottyBlades Oct 09 '20 at 17:05
  • `Make sure the type of arguments match in the call site and implementation` - helped – Rahul Gaur Feb 05 '21 at 07:07
39

Not an answer to this question, but as I came here looking for the error others might find this also useful:

For me, I got this Swift error when I tried to use the for (index, object) loop on an array without adding the .enumerated() part ...

TheEye
  • 8,809
  • 2
  • 38
  • 56
  • I too am not answering this question, but i received this error on SWIFT 5, XCode 12 when I was passing a CGFloat to a function that called for an Objc float. When I cast the CGFloat to float, the error went away. Not the most descriptive error, ambiguous usually refers to repeating or the same. – PDG Sep 30 '20 at 17:11
  • @PDG Along the same lines, for me, the value I was passing in didn't actually exist. Also, ambiguous is defined as "Open to more than one interpretation. Doubtful or uncertain." – ReinstateMonica3167040 Oct 09 '20 at 18:37
20

The compiler can't figure out what type to make the Dictionary, because it's not homogenous. You have values of different types. The only way to get around this is to make it a [String: Any], which will make everything clunky as all hell.

return [
    "title": title,
    "is_draft": isDraft,
    "difficulty": difficulty,
    "duration": duration,
    "cost": cost,
    "user_id": userId,
    "description": description,
    "to_sell": toSell,
    "images": [imageParameters, imageToDeleteParameters].flatMap { $0 }
] as [String: Any]

This is a job for a struct. It'll vastly simplify working with this data structure.

Alexander
  • 48,074
  • 8
  • 78
  • 121
  • Changing to a struct was the right call for me. I was trying to pass a tuple back from a Promise to another Promise and was getting this error; converting to a struct made the compiler happy. I kind of liked the look of the code using a tuple, but not a big deal--it's nicer having it compile. – James Toomey Mar 24 '18 at 16:31
  • @JamesToomey Well tuples can't conform to protocols, so if passing a value through a promise requires some protocol conformance, tuples can't fit that requirement. Anytime you see a mapping of fixed keys to values, you should almost always think "I should use a struct/class" here. – Alexander Mar 24 '18 at 20:08
  • @Alexander, if the Promise allows you to pass back a generic type like this: `func RunAndReturnPromise() -> Promise`, is a tuple not considered a valid type in that case? – James Toomey Mar 25 '18 at 06:27
  • @JamesToomey That depends on whether `Promise` imposes any constraints over its generic parameter `T`. – Alexander Mar 25 '18 at 07:26
  • @Alexander, I did more reading/experiments, and I guess I was writing the return parameter wrong. If I declare `Promise< (String, Int) >` and then `resolve( ("a", 5) )`, it works. I still think in the long run the code is clearer with `Promise`, so I'm glad I came across your answer. Like you mentioned in your original answer, when the data type gets more and more complicated, as mine probably will, it's so much easier to abstract it to a struct or class. – James Toomey Mar 25 '18 at 14:17
  • @JamesToomey Well you *could* use `typaalias MyCustomTuple = (String, Int)`, but the problem is that fundamentally, tuples are not well suited to encapsulating data. The issue is that your `MyCustomTuple` is an equivalent type to every other `(String, Int)`. So you have no way to enforce any invariant within your type. Suppose you had a type that contained an array, and a `max` element of the array that you cache to improve performance. Such a type needs to ensure that all operations on it preserve this invarient (that that cached max element is updated appropriately as the array changed)... – Alexander Mar 25 '18 at 15:14
  • You have your `typealias ArrayMaxCacher = (array: [T], max: T)`, and I could just come along and say `let arrayMaxCacher = (array: [1, 2, 3], max: 0])`. Now what? All code that was predicated on the invariant that `max` is the maximal element of `array`, won't work correctly anymore. This is where structs excel. Two structs, even if they both contain the same named/typed members, are different types. I could only make such a struct using the public initializer you provide for me, which you can use to appropriately set the private `array`/`max` variables. Invariants enforced. – Alexander Mar 25 '18 at 15:17
  • Don't get me wrong, that's not to say tuples don't have a purpose. They're a fantastically useful language feature, and one of my favorite things in Swift, e.g. for multiple return values, or transporting intermediate data between chains of calls (e.g. `enumerated()`'s result). I'm currently working with Java, and the lack of tuples is making stream usage extremely clunky. – Alexander Mar 25 '18 at 15:18
  • @Alexander, that totally makes sense. It gives me a better sense of when a tuple is more appropriate to use. Sometimes it's hard to get that from an explanation of _how_ a feature works, but not necessarily _when_ it's a better fit (or not). – James Toomey Mar 26 '18 at 17:02
10

I had this message when the type of a function parameter didn't fit. In my case it was a String instead of an URL.

brainray
  • 11,429
  • 10
  • 60
  • 109
  • Same. Function needed an Int64, so when I hard-coded just "param: 1" it was happy, but when I used a constant that was declared without the Int64 piece & was just an integer, "param: myConstantOne" caused the error. Not, of course, at the parameter, but at the function call. As others have noted, less than perfectly helpful, that! – ConfusionTowers Nov 27 '20 at 01:13
5

I got this error when I put a space before a comma in the parameters when calling a function.

eg, I used:

myfunction(parameter1: parameter1 , parameter2: parameter2)

Whereas it should have been:

myfunction(parameter1: parameter1, parameter2: parameter2)

Deleting the space got rid of the error message

narco
  • 750
  • 4
  • 20
3

Explicitly declaring the inputs for that mapping function should do the trick:

let imageToDeleteParameters  = imagesToDelete.map {
    (whatever : WhateverClass) -> Dictionary<String, Any> in
    ["id": whatever.id, "url": whatever.url.absoluteString, "_destroy": true]
}

Substitute the real class of "$0" for "WhateverClass" in that code snippet, and it should work.

Jeremy Gurr
  • 1,483
  • 6
  • 10
2

In my case, this error message shown when I don't added optional property to constructor.

struct Event: Identifiable, Codable {

    var id: String
    var summary: String
    var description: String?
    // also has other props...

    init(id: String, summary: String, description: String?){
        self.id = id
        self.summary = summary
        self.description = description
    }
}

// skip pass description
// It show message "Type of expression is ambiguous without more context"
Event(
    id: "1",
    summary: "summary",
)

// pass description explicity pass nil to description
Event(
    id: "1",
    summary: "summary",
    description: nil
)

but it looks always not occured.

I test in my playground this code, it show warning about more concrete

var str = "Hello, playground"
struct User {
    var id: String
    var name: String?
    init(id: String, name: String?) {
        self.id = id
        self.name = name
    }
}

User(id: "hoge") // Missing argument for parameter 'name' in call
Matsumoto Kazuya
  • 942
  • 10
  • 15
2

You have two " " before the =

let imageToDeleteParameters = imagesToDelete.map { ["id": $0.id, "url": $0.url.absoluteString, "_destroy": true] }
shocking
  • 599
  • 12
  • 15
2

For me the case was Type inference I have changed the function parameters from int To float but did not update the calling code, and the compiler did not warn me on wrong type passed to the function

Before

func myFunc(param:Int, parma2:Int) {}

After

func myFunc(param:Float, parma2:Float) {}

Calling code with error

var param1:Int16 = 1
var param2:Int16 = 2
myFunc(param:param1, parma2:param2)// error here: Type of expression is ambiguous without more context

To fix:

var param1:Float = 1.0f
var param2:Float = 2.0f
myFunc(param:param1, parma2:param2)// ok!
benchuk
  • 599
  • 6
  • 10
1

In my case it happened with NSFetchedResultsController and the reason was that I defined the NSFetchedResultsController for a different model than I created the request for the initialization (RemotePlaylist vs. Playlist):

  var fetchedPlaylistsController:NSFetchedResultsController<RemotePlaylist>!

but initiated it with a request for another Playlist:

let request = Playlist.createFetchRequest()
fetchedPlaylistsController = NSFetchedResultsController(fetchRequest: request, ...
randomcontrol
  • 992
  • 8
  • 8
1

In my case, I ran into this error when I was creating a distribution build, and a class was referring to Debug only context method.

Something like this. Try compiling the below class for the Distribution build.

class MyClass {
   func sayHello() {
      helloWorld()
    }
    
   #if DEBUG
    func helloWorld() {
         print("Hello world")
    }
   #endif
}
Sada
  • 1,298
  • 3
  • 14
0

In my case, the arguments I was passing had optional String values. Providing a default value to fall back on ( in case the value is nil ) solved this issue for me.

I changed this -

router?.pushToDetailsScreen(
gitHubRepositoryOwnerName: gitHubRepositoryDetails?[index].owner?.login,
gitHubRepositoryName: gitHubRepositoryDetails?[index].name,
avatarUrl: gitHubRepositoryDetails?[index].owner?.avatar_url)

to this -

router?.pushToDetailsScreen(
gitHubRepositoryOwnerName: gitHubRepositoryDetails?[index].owner?.login ?? "",
gitHubRepositoryName: gitHubRepositoryDetails?[index].name ?? "",
avatarUrl: gitHubRepositoryDetails?[index].owner?.avatar_url ?? "")
-1

As theEye's answer it is not an answer to this question, but as I also came here looking for the error im posting my case as others might find this also useful:

I got this error message when I was by error trying to calculate a value of two different types.

In my case I was trying to divide a CGFloat by a Double

BeSCoNeZ
  • 21
  • 4
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). You can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question once you have enough [reputation](https://stackoverflow.com/help/whats-reputation). – Mhd Alaa Alhaj Jan 24 '21 at 10:26