'Cannot convert value of type 'User?' to expected argument type 'UserId?' (aka 'Optional<String>')

When creating a function to addDetail, passing the userId as the userId. It comes up with this error:

Cannot convert value of type 'User?' to expected argument type 'UserId?' (aka 'Optional')

I have tried force unwrapping the userId in the addDetail function, but that didn't work.

func sendAction(action: Action)
    {
        switch action
        {
        case .addDetail:
            userService.currentUser().flatMap { userId -> AnyPublisher<Void, Error> in
                return self.addDetail(userId: userId) // Error Here
            }.sink { completion in
                switch completion
                {
                case let .failure(error):
                    print(error.localizedDescription)
                case .finished:
                    print("Finished")
                }

            } receiveValue: { _ in
                print("Success")
            }.store(in: &cancellables)
        }
    }

    private func addDetail(userId: UserId?) -> AnyPublisher<Void, Error>
    {
        guard let vehicleType = vehicleDropDown.text,
              let detailType = detailDropDown.text,
              let coatingType = coatingDropDown.text else { return Fail(error: NSError()).eraseToAnyPublisher() }

        let detail = Detail(vehicleType: vehicleType, detailType: detailType, coatingType: coatingType, userId: userId!)

        return detailService.create(detail).eraseToAnyPublisher()
    }
}

I'm really not sure why this error is coming up since it makes sense to me, I may have missed something in the rest of the code that needs more experienced eyes.

Rest of code:

import SwiftUI
import Combine
import FirebaseFirestore

typealias UserId = String

final class AddDetailViewModel: ObservableObject
{
    @Published var vehicleDropDown = DetailPartViewModel(type: .vehicleType)
    @Published var detailDropDown = DetailPartViewModel(type: .detailType)
    @Published var coatingDropDown = DetailPartViewModel(type: .coatingType)

    private let userService : UserServiceProtocol
    private var cancellables : [AnyCancellable] = []
    private let detailService : DetailServiceProtocol

    enum Action
    {
        case addDetail
    }

    init(userService: UserServiceProtocol = UserService(), detailService: DetailServiceProtocol = DetailService())
    {
        self.userService = userService
        self.detailService = detailService
    }

    func sendAction(action: Action)
    {
        switch action
        {
        case .addDetail:
            userService.currentUser().flatMap { userId -> AnyPublisher<Void, Error> in
            return self.addDetail(userId: userId)
            }.sink { completion in
                switch completion
                {
                case let .failure(error):
                    print(error.localizedDescription)
                case .finished:
                    print("Finished")
                }

            } receiveValue: { _ in
                print("Success")
            }.store(in: &cancellables)
        }
    }

    private func addDetail(userId: UserId?) -> AnyPublisher<Void, Error>
    {
        guard let vehicleType = vehicleDropDown.text,
              let detailType = detailDropDown.text,
              let coatingType = coatingDropDown.text else { return Fail(error: NSError()).eraseToAnyPublisher() }

        let detail = Detail(vehicleType: vehicleType, detailType: detailType, coatingType: coatingType, userId: userId!)

        return detailService.create(detail).eraseToAnyPublisher()
    }
}

extension AddDetailViewModel
{
    struct DetailPartViewModel: DropDownItemProtocol
    {
        var selectedOption: DropDownOptions

        var options: [DropDownOptions]

        var headerTitle: String
        {
            type.rawValue
        }

        var dropDownTitle: String
        {
            selectedOption.formatted
        }

        var isSelected: Bool = false

        private let type : DetailPartType

        init(type: DetailPartType)
        {
            switch type
            {
                case .vehicleType:
                    self.options = vehicleTypeOptions.allCases.map { $0.toDropDownOption }

                case .detailType:
                    self.options = detailTypeOptions.allCases.map { $0.toDropDownOption }

                case .coatingType:
                    self.options = coatingTypeOptions.allCases.map { $0.toDropDownOption }
            }
            self.type = type
            self.selectedOption = options.first!
        }

        enum DetailPartType: String, CaseIterable
        {
            case vehicleType = "Type of Vehicle"
            case detailType = "Type of Job"
            case coatingType = "Type of Coating"
        }

        enum vehicleTypeOptions: String, CaseIterable, DropDownOptionProtocol
        {
            case Sedan
            case Coupe
            case Wagon
            case Hatchback
            case Convertible
            case Suv
            case Minivan
            case Ute
            case Supercar

            var toDropDownOption: DropDownOptions
            {
                .init(type: .text(rawValue), formatted: rawValue.capitalized)
            }
        }

        enum detailTypeOptions: String, CaseIterable, DropDownOptionProtocol
        {
            case Basic
            case Full

            var toDropDownOption: DropDownOptions
            {
                .init(type: .text(rawValue), formatted: rawValue.capitalized)
            }
        }

        enum coatingTypeOptions: String, CaseIterable, DropDownOptionProtocol
        {
            case Ceramic
            case Hydrophobic
            case None

            var toDropDownOption: DropDownOptions
            {
                .init(type: .text(rawValue), formatted: rawValue.capitalized)
            }
        }

    }
}

extension AddDetailViewModel.DetailPartViewModel
{
    var text: String?
    {
        if case let .text(text) = selectedOption.type
        {
            return text
        }
        else
        {
            return nil
        }
    }
}

protocol DropDownItemProtocol
{
    var options : [DropDownOptions] { get }
    var headerTitle : String { get }
    var dropDownTitle : String { get }
    var isSelected : Bool { get set }
    var selectedOption : DropDownOptions { get set }
}

protocol DropDownOptionProtocol
{
    var toDropDownOption : DropDownOptions { get }
}

struct DropDownOptions
{
    enum DropDownOptionType
    {
        case text(String)
        case number(Int)
    }

    let type : DropDownOptionType
    let formatted : String
}

struct Detail: Codable
{
    let vehicleType : String
    let detailType : String
    let coatingType : String
    let userId : String
}

protocol DetailServiceProtocol
{
    func create(_ detail: Detail) -> AnyPublisher<Void, Error>
}

final class DetailService: DetailServiceProtocol
{
    private let db = Firestore.firestore()

    func create(_ detail: Detail) -> AnyPublisher<Void, Error>
    {
        return Future<Void, Error>
        {
            promise in

            _ = self.db.collection("details").addDocument(data: [
                "\(detail.vehicleType)": "\(detail.vehicleType)",
                "\(detail.detailType)": "\(detail.detailType)",
                "\(detail.coatingType)": "\(detail.coatingType)"
            ]) { err in
                if let err = err {
                    print("Error adding document: \(err)")
                } else {
                    print("Document added with ID: ")
                }
            }

        }.eraseToAnyPublisher()
    }
}

Any Help will be much appreciated!

EDIT (UserService Object):

protocol UserServiceProtocol
{
    func currentUser() -> AnyPublisher<User?, Never>
}

class UserService: UserServiceProtocol, ObservableObject
{
    func currentUser() -> AnyPublisher<User?, Never>
    {
        Just(Auth.auth().currentUser).eraseToAnyPublisher()
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source