I’m trying to call this function from an objective C class but am running into an issue. I get the error "Method cannot be marked @objc because the type of the parameter 3 cannot be represented in Objective-C"

I’m assuming the issue is with the “Error” parameter but don’t understand what the issue is exactly. Everything I’ve read has been dealing with Swift 1 or 2 but this is in Swift 5.

class NetworkManager {
    
    struct DataModel: Decodable {
        let id: String
        let carID: String
        let ownerId: String
        let locationId: String?
        let status: String
    }
    
    static let shared = NetworkManager()
    
    @objc static func fetchCarWarranty(for carID: String, ownerID: String, completed: @escaping (Result<[DataModel], Error>) -> Void) {
        
        let urlString = APIURL
        let session = URLSession.shared
        let url = URL(string: urlString)!
        var request = URLRequest(url: url)
        
        let task = session.dataTask(with: request) { data, response, error in
            
            if let _ = error {
                completed(.failure(error!))
                return
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                completed(.failure(error!))
                return
            }
            
            guard let data = data else {
                return
            }
            
            do {
                let decoder = JSONDecoder()
                let dataModel = try decoder.decode([DataModel].self, from: data)
                completed(Result.success(dataModel))
            } catch {
                completed(.failure(error))
            }
        }
        task.resume()
    }
}

I've thought of calling fetchCarWarranty from a second Swift function but not sure if this is a good practice to follow or not.

Also, how can I get the value from this function if I'm calling it from an objective-c file/ class?

1

There are 1 best solutions below

5
Larme On

You can't use Result in Objective-C, you need a closure like the one for dataTask(with:completion:), with optional parameters like error to indicate it's not in "error case":

//Call that one when using Objective-C
@objc static func fetchCarWarranty(for carID: String, ownerID: String, completed: @escaping (([DataModel]?, Error?) -> Void)) {
    fetchCarWarranty(for: carID, ownerID: ownerID) { result in
        switch result {
        case .success(let models):
            completed(models, nil)
        case .failure(let error):
            completed(nil, error)
        }
    }
}

//Call that one when using Swift
static func fetchCarWarranty(for carID: String, ownerID: String, completed: @escaping (Result<[DataModel], Error>) -> Void) {
    // Your method "Swifty" code
}

I did this for an app with legacy Objective-C code. You call in the end the Swifty method with Result internally. so the code does the same.

You could also for the Objective-C compatible one have to closures:

@objc static func fetchCarWarranty(for carID: String, ownerID: String, success: @escaping (([DataModel]) -> Void), failure: @escaping ((Error) -> Void)) {
    fetchCarWarranty(for: carID, ownerID: ownerID { result in 
        switch result {
            case .success(let models): 
                success(models)
            case .failure(let error):
                failure(error)
        }
    }
}

Now, the issue is that DataModel is a struct. If you replace that with:

// With temp [String] parameter instead of [DataModel]
@objc static func fetchCarWarranty(for carID: String, ownerID: String, completed: @escaping (([String]?, Error?) -> Void)) {
    fetchCarWarranty(for: carID, ownerID: ownerID) { result in
        switch result {
        case .success(let models):
            completed(["models"], nil) //Fake output
        case .failure(let error):
            completed(nil, error)
        }
    }
}

It works, because String is visible by Objective-C. But you can't see a Swift struct from Objective-C. See the documentation, or one the related questions:

Is there a way to use Swift structs in Objective-C without making them Classes?
How to pass a Swift struct as a parameter to an Objective-C method
How to use Swift struct in Objective-C