Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you call an Objective-C variadic method from Swift?

Supposing I have a class in Objective-c with a static method like this:

+ (NSError *)executeUpdateQuery:(NSString *)query, ...; 

How do I call that from Swift? The autocomplete doesn't recognise it, and the compiler is unhappy with:

MyClassName.executeUpdateQuery("") 

Complaining that 'MyClassName.Type does not have a member named executeUpdateQuery'

like image 542
rustyshelf Avatar asked Jun 13 '14 00:06

rustyshelf


People also ask

What is a variadic function in Swift?

Variadic functions are functions that accept any number of parameters. The most common one in Swift is print() – most people use it to print a single value, but you can actually pass as many as you want, like this: print(1, 2, 3, 4, 5) To make a variadic function of your own, just add ... after any parameter.

How do you access Variadic arguments?

To access variadic arguments, we must include the <stdarg. h> header.

Why do we use variadic parameters in Swift?

A variadic parameter accepts zero or more values of a specified type. You use a variadic parameter to specify that the parameter can be passed a varying number of input values when the function is called.

What symbols are used in a function declaration to indicate that it is a variadic function?

A function with a parameter that is preceded with a set of ellipses ( ... ) is considered a variadic function. The ellipsis means that the parameter provided can be zero, one, or more values. For the fmt. Println package, it is stating that the parameter a is variadic.


2 Answers

Write a va_list version of your variadic method;

+ (NSError *)executeUpdateQuery:(NSString *)query, ... {     va_list argp;     va_start(argp, query);     NSError *error = [MyClassName executeUpdateQuery: query args:argp];     va_end(argp);          return error; }  + (NSError *)executeUpdateQuery:(NSString *)query args:(va_list)args {     NSLogv(query,args);     return nil; } 

This can then be called from Swift

MyClassName.executeUpdateQuery("query %d, %d %d", args: getVaList([1,2,3,4])) 

Add an extension to support native Swift variadic args:

protocol CFormatFunction {     class func executeUpdateQuery(_ format: String, _ args: CVarArg...) -> NSError? }  extension MyClassName : CFormatFunction {     class func executeUpdateQuery(_ format: String, _ args: CVarArg...) -> NSError?     {         return withVaList(args) { MyClassName.executeUpdateQuery(format, args: $0) }     } }  MyClassName.executeUpdateQuery("query %d %@ %.2f", 99, "Hello", 3.145) 

Be careful, Swift doesn't provide NS_FORMAT_FUNCTION warnings (-Wformat)

MyClassName.executeUpdateQuery("query %@", 99) 
like image 127
Simon Avatar answered Sep 22 '22 13:09

Simon


CVArgType is useful in presenting C "varargs" APIs natively in Swift. (Swift Docs)

If you have

+ (int)f1:(int)n, ...; 

you first need to make a va_list version:

+ (int)f2:(int)n withArguments:(va_list)arguments 

This can be done without duplicating code by calling the va_list version from the variadic version. If you didn't write the original variadic function it may not be possible (explained in this reference).

Once you have this method, you can write this Swift wrapper:

func swiftF1(x: Int, _ arguments: CVarArgType...) -> Int {      return withVaList(arguments) { YourClassName.f2(x, withArguments :$0) } } 

Note the omitted external parameter name (_ before arguments), which makes the call syntax for swiftF1 just like a normal C variadic function:

swiftF1(2, some, "other", arguments) 

Note also that this example doesn't use getVaList because the docs say it is "best avoided."

You can further put this function in a Swift extension of the original class, if you want.

like image 25
divergio Avatar answered Sep 21 '22 13:09

divergio