Argument Type “String” Does Not Conform To Expected Type “Decoder” | Swift Fix Steps

In Swift, argument type “string” does not conform to expected type “decoder” means a String is passed where init(from:) expects a Decoder value.

What This Decoder Error Message Really Means

Swift shows this message when it sees a function or initializer that expects a value conforming to the Decoder protocol, yet it receives a plain String value instead. The compiler cannot convert a text value into a decoding engine on its own, so it stops with this compile time error.

The message often appears in projects that use Codable models or JSONDecoder. In those cases the code usually calls the wrong initializer, or the visible initializers on a type do not match what the call site tries to use.

Once you know where Swift expects a Decoder and why a text value sits in that spot, the fix is usually small: change the initializer you call, pass a Data value instead of text, or add a public initializer that takes the fields you want.

The text of the error helps you pinpoint the problem. The phrase argument type refers to the value you pass into a function call. The phrase expected type "Decoder" describes the type that the function needs instead. Swift is telling you that a call passes a plain text value where a decoding context should stand.

  • Review The Generic Type Parameters — If the function is generic over T: Decodable, the site that fails may be feeding in a type that does not match the generic constraint.
  • Scan The Full Error List — This message often ships with sibling notes that point out which parameter mismatches the expected Decoder.
  • Check The Return Type As Well — A return type that wraps decoded data, such as a model or array of models, can hint at whether decoding belongs inside that function.

Why Codable Types In Another Target Often Trigger It

One classic way to hit this error is with a simple Codable struct that lives in a framework or separate module. The type looks harmless, yet the project will not build.

public struct Preference: Codable {
    public let id: String
}

At a first glance it seems natural to create a value with a single argument initializer.

let preference = Preference(id: "cool")

Inside the same module this call works, because Swift synthesizes an internal memberwise initializer that accepts an id: String argument. When the type also conforms to Decodable, the compiler synthesizes a public initializer of the form init(from decoder: Decoder), which is used by decoding engines such as JSONDecoder.

The problem appears when another target imports this framework. From that target the internal memberwise initializer is not visible. The only public initializer is the one that accepts a Decoder. When the call site writes Preference(id: "cool"), the compiler tries to match it against init(from: Decoder), then complains that the argument type String does not match the expected Decoder type.

  • Add A Public Memberwise Initializer — Declare public init(id: String) inside the struct so both modules can build instances directly.
  • Keep The Synthesized Decoder Initializer — Leave init(from: Decoder) alone so JSONDecoder can still decode the type from data.
public struct Preference: Codable {
    public let id: String

    public init(id: String) {
        self.id = id
    }
}

Argument Type “String” Does Not Conform To Expected Type “Decoder” In Codable Structs

When you see argument type “string” does not conform to expected type “decoder” for a simple model type, start by checking which initializers exist and which of them are visible to the file that fails. In modular projects this visibility detail matters.

  • Check Memberwise Access Level — If the type lives in a framework, make the memberwise initializer public when you need to call it from app code.
  • Inspect Autocomplete Suggestions — Autocomplete sometimes inserts init(from:) instead of the memberwise initializer; adjust the call to use id: or other field labels.
  • Confirm The Call Site Target — Code in tests or helper tools may live in another module that cannot see internal initializers.

If this inspection shows that you only have init(from: Decoder) visible, add a custom initializer that exposes the fields you need. With that in place the ambiguous match vanishes and the compiler can pick the correct initializer again.

When JSONDecoder.decode Shows The Same Message

The same text can appear when you misuse JSONDecoder. The decode(_:from:) method expects a type that conforms to Decodable along with a Data value, not a plain text value.

let json = "{ \"id\": \"cool\" }"

// Wrong: String passed where Data is required
let decoded = JSONDecoder().decode(Preference.self, from: json)

In this case the compiler complains for a different reason. The method parameter named from expects Data, and the overload that accepts Decoder expects a decoding engine. Passing a String can confuse overload resolution and you end up with the same error message.

  • Convert JSON Text To Data First — Use Data(json.utf8) or similar so the call passes a byte buffer, not a text value.
  • Match The decode Signature Exactly — The method takes the meta type as the first argument and a Data value in the from parameter.
  • Check Generic Utility Functions — Shared decode helpers must accept Data or Decoder types explicitly instead of overloading on String.
let json = "{ \"id\": \"cool\" }"
let data = Data(json.utf8)

// Correct
let decoded = try JSONDecoder().decode(Preference.self, from: data)

This pattern often appears when you copy sample JSON text from a web response or file and store it in a Swift string. The code feels natural, yet JSONDecoder never sees the raw bytes that it expects. Converting the sample into Data keeps the types aligned with the decoding APIs.

Network stacks already hand you data in the correct format. A URLSession callback delivers a Data? value, so you can feed that value straight into decode after unwrapping. Problems tend to appear when code converts that data back into a String just to log or inspect it, then tries to use the text for decoding.

  • Log Both Text And Data Length — When you print JSON for debugging, keep a copy of the original Data so decoding still uses the correct type.
  • Avoid Chained Conversions Through String — Passing information through multiple text conversions can hide encoding issues and trigger type mismatches.
  • Keep Helper Functions Close To URLSession — A small wrapper that accepts a request and returns decoded models keeps data handling and decoding rules together.

When the call site passes a data buffer instead of text, the compiler no longer tries to treat a String as a Decoder. The method selection is clear and the error disappears.

Fixing The String Argument Type Vs Decoder Type Mismatch

While the message mentions conforming to Decoder, the real issue almost always comes from a mismatch between the expected parameter type and what the call site passes. The fixes fall into a few predictable groups, each linked to a common pattern in Swift projects.

Context Cause Fix
Framework model type Only init(from: Decoder) is public Add a public memberwise initializer
JSON decoding helper String passed to decode Convert to Data before decoding
Custom initializer Wrong parameter label or signature Update labels to match call sites
  • Review The Function Signature — Read the declaration that the compiler chooses and confirm its parameter types and labels.
  • Search For init(from: Decoder) — Every Decodable type gains or defines this initializer, and it often sits at the center of the error.
  • Align Call Sites With Declarations — Adjust labels and argument types so they match the selected initializer exactly.

Watch For Initializers With A from Label

Some types provide several initializers that share a from label. One version might accept a Decoder, while another accepts a value such as CGFloat or Double. If autocompletion picks the wrong overload, the compiler can show the decoder message even when your intent has nothing to do with decoding.

  • Read The Full Parameter List — When a suggestion appears in autocomplete, pause long enough to check every parameter type, not just the labels.
  • Prefer Clear Factory Methods — Static helpers with names such as fromJSONString or fromUserDefaults reduce confusion with decoding initializers.

Once every call passes values that match their declarations, the compiler no longer tries to cast a String into a decoder and the code builds normally.

Step By Step Checklist When The Error Will Not Go Away

On larger code bases the message can appear in surprising places. A small helper function or extension can drag the wrong overload into play and keep this compiler message on screen across several files. A systematic walk through the call chain usually solves it.

  1. Locate The Exact Failing Call — Click the error in the sidebar and let Xcode show the function or initializer call that triggers it.
  2. Command Click The Type Or Method Name — Jump to the declaration so you can see the full list of overloads and their parameter types.
  3. Check For Decoder Initializers — Look for init(from: Decoder) and confirm whether your call might be matching that initializer by mistake.
  4. Inspect Generic Helpers — If the method is generic, confirm that the constraints still fit the values you pass.
  5. Clean And Rebuild The Project — Old build artefacts can confuse Xcode; a fresh build helps confirm that the fix is real.

During this review you might spot helper functions that accept a Decoder where a Data buffer would make more sense. In those cases adjust the helper signature so that call sites pass the right type directly instead of forcing Swift to guess.

Preventing Decoder Type Errors In Future Projects

Once the project builds again, take a short moment to reduce the chances of hitting the same message later. Small habits around initializers, module boundaries, and decoding helpers make these issues far rarer.

  • Design Clear Initializers For Models — Give core types explicit public initializers with field labels so call sites never depend on synthesized access levels.
  • Keep Decoding Bundled In One Place — Centralize JSONDecoder usage in a small set of functions that always accept Data, not String.
  • Avoid Overloaded Helpers With Loose Types — Use precise signatures so the compiler never has to guess between Decoder, Data, and text.
  • Add Lightweight Unit Tests — Tests that decode known payloads catch mismatches early, long before an error lands in app code.

Regular code review sessions help as well; a teammate can often spot a strange initializer or helper signature before it reaches a shared library.

With these patterns in place, the main keyword can appear rarely, and when this decoder error turns up again you will have a clear path to track it down and correct it.