How to call Swift 5.3 code from Objective-C

The first Apple Silicon Mac with the M1 chip is finally here. It seems that the pros and cons are swirling on the net.

I'm currently a Mac user, but rather than replacing my Mac, I'm looking at its potential application to satellite projects I'm working on. I expect that the image processing performance per price of the new Mac is extremely high, and the image processing performance per power consumption is also good. I will write about the artificial satellite project in Elixir Advent Calendar 2020, so please look forward to it.

So, instead of developing an iOS app or Mac app, using the mechanism to call the C program called NIF from Elixir, call the Swift code assets accumulated by Apple such as Core Image and Core ML from Elixir. I wrote this article with the aim of seeing if I could do it. If you can call Swift from Objective-C, you can call Objective-C from C, so you can also call Swift from Elixir.

Regarding Swift, except for the Apple Developer Program, there were only articles with old language specifications, so this article corresponding to Swift 5.3 adopted in Xcode 12.1, which is the latest Xcode as of 2020.11.12, is similar. I think there is something new about the article.

I'm also compiling Swift / Objective-C code using make on the command line instead of Xcode. I think the way to write Makefile is also helpful.

The finished code

Share the code right away. It is published on GitHub with Apache-2.0 License.

https://github.com/zacky1972/swift_objc_test

Swift code (callee)

Here's the Swift code I want to call:

ExampleClass.swift


import Foundation

@objc class ExampleClass: NSObject {
	var count = 0
	@objc func increment() {
		count += 1
		NSLog("increment")
	}
	@objc func increment(by amount: Int) {
		count += amount
	}
	@objc func reset() {
		count = 0
	}
}

The source of this code is here. https://docs.swift.org/swift-book/LanguageGuide/Methods.html

Objective-C code (caller)

To be able to call it from Objective-C, do the following two things.

The code on the Objective-C side looks like this.

main.m


#import <Foundation/Foundation.h>
#import "ExampleClass-Swift.h"

int main(int argc, char** argv)
{
	ExampleClass *obj = [[ExampleClass alloc] init];
	[obj increment];
	NSLog(@"Testing");
}

The points are as follows:

All you have to do is read it as Objective-C and call it normally.

Execution result

The execution result is as follows.

% ./hello 
2020-11-12 18:47:57.204 hello[10554:225821] increment
2020-11-12 18:47:57.204 hello[10554:225821] Testing

As expected, since the increment method was called earlier, it says Testing.

That's all the code to prepare.

Makefile

Let's take a look at the Makefile to see how to build it.

Makefile


.phony: all clean

all: hello


main.o: main.m ExampleClass-Swift.h
	clang -c $< -o $@

ExampleClass.o: ExampleClass.swift ExampleClass-Swift.h
	swiftc -emit-object -parse-as-library $<

ExampleClass-Swift.h: ExampleClass.swift
	swiftc $< -emit-objc-header -emit-objc-header-path $@

hello: main.o ExampleClass.o
	swiftc $^ -o $@ -framework Foundation

clean:
	$(RM) hello *.o *.{swiftdoc,swiftmodule,swiftsourceinfo} ExampleClass ExampleClass-Swift.h

The points are as follows:

Generated Objective-C header file

The generated Objective-C header file is:

ExampleClass-Swift.h


// Generated by Apple Swift version 5.3 (swiftlang-1200.0.29.2 clang-1200.0.30.1)
#ifndef EXAMPLECLASS_SWIFT_H
#define EXAMPLECLASS_SWIFT_H
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"

#if !defined(__has_include)
# define __has_include(x) 0
#endif
#if !defined(__has_attribute)
# define __has_attribute(x) 0
#endif
#if !defined(__has_feature)
# define __has_feature(x) 0
#endif
#if !defined(__has_warning)
# define __has_warning(x) 0
#endif

#if __has_include(<swift/objc-prologue.h>)
# include <swift/objc-prologue.h>
#endif

#pragma clang diagnostic ignored "-Wauto-import"
#include <Foundation/Foundation.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#if !defined(SWIFT_TYPEDEFS)
# define SWIFT_TYPEDEFS 1
# if __has_include(<uchar.h>)
#  include <uchar.h>
# elif !defined(__cplusplus)
typedef uint_least16_t char16_t;
typedef uint_least32_t char32_t;
# endif
typedef float swift_float2  __attribute__((__ext_vector_type__(2)));
typedef float swift_float3  __attribute__((__ext_vector_type__(3)));
typedef float swift_float4  __attribute__((__ext_vector_type__(4)));
typedef double swift_double2  __attribute__((__ext_vector_type__(2)));
typedef double swift_double3  __attribute__((__ext_vector_type__(3)));
typedef double swift_double4  __attribute__((__ext_vector_type__(4)));
typedef int swift_int2  __attribute__((__ext_vector_type__(2)));
typedef int swift_int3  __attribute__((__ext_vector_type__(3)));
typedef int swift_int4  __attribute__((__ext_vector_type__(4)));
typedef unsigned int swift_uint2  __attribute__((__ext_vector_type__(2)));
typedef unsigned int swift_uint3  __attribute__((__ext_vector_type__(3)));
typedef unsigned int swift_uint4  __attribute__((__ext_vector_type__(4)));
#endif

#if !defined(SWIFT_PASTE)
# define SWIFT_PASTE_HELPER(x, y) x##y
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
#endif
#if !defined(SWIFT_METATYPE)
# define SWIFT_METATYPE(X) Class
#endif
#if !defined(SWIFT_CLASS_PROPERTY)
# if __has_feature(objc_class_property)
#  define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
#  define SWIFT_CLASS_PROPERTY(...)
# endif
#endif

#if __has_attribute(objc_runtime_name)
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
#else
# define SWIFT_RUNTIME_NAME(X)
#endif
#if __has_attribute(swift_name)
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
#else
# define SWIFT_COMPILE_NAME(X)
#endif
#if __has_attribute(objc_method_family)
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
#else
# define SWIFT_METHOD_FAMILY(X)
#endif
#if __has_attribute(noescape)
# define SWIFT_NOESCAPE __attribute__((noescape))
#else
# define SWIFT_NOESCAPE
#endif
#if __has_attribute(ns_consumed)
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
#else
# define SWIFT_RELEASES_ARGUMENT
#endif
#if __has_attribute(warn_unused_result)
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
# define SWIFT_WARN_UNUSED_RESULT
#endif
#if __has_attribute(noreturn)
# define SWIFT_NORETURN __attribute__((noreturn))
#else
# define SWIFT_NORETURN
#endif
#if !defined(SWIFT_CLASS_EXTRA)
# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
#  define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
#  define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# else
#  define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
#  define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# endif
#endif
#if !defined(SWIFT_RESILIENT_CLASS)
# if __has_attribute(objc_class_stub)
#  define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
#  define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
# else
#  define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
#  define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif

#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif

#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif

#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
#  define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
#  define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
# if defined(__has_attribute) && __has_attribute(enum_extensibility)
#  define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
#  define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# if __has_feature(generalized_swift_name)
#  define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# else
#  define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
#endif
#if !defined(SWIFT_UNAVAILABLE)
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
#endif
#if !defined(SWIFT_UNAVAILABLE_MSG)
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
#endif
#if !defined(SWIFT_AVAILABILITY)
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
#endif
#if !defined(SWIFT_WEAK_IMPORT)
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#endif
#if !defined(SWIFT_DEPRECATED)
# define SWIFT_DEPRECATED __attribute__((deprecated))
#endif
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
#if __has_feature(attribute_diagnose_if_objc)
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
#else
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
#endif
#if !defined(IBSegueAction)
# define IBSegueAction
#endif
#if __has_feature(modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
@import ObjectiveC;
#endif

#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"

#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="ExampleClass",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif


SWIFT_CLASS("_TtC12ExampleClass12ExampleClass")
@interface ExampleClass : NSObject
- (void)increment;
- (void)incrementBy:(NSInteger)amount;
- (void)reset;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#pragma clang diagnostic pop
#endif

Summary

This finding makes it possible to call Swift 5.3 code from Objective-C. By writing a separate code that calls Objective-C from C, you can call Swift from C. In other words, you can freely call Swift and Apple APIs from Elixir. Banzai!

Acknowledgments

This research result was supported by A-STEP Tryout JPMJTM20H1 which is a support program for optimal development of research results in the research results development project of the Japan Science and Technology Agency.

Recommended Posts

How to call Swift 5.3 code from Objective-C
How to call AmazonSQSAsync
How to implement UICollectionView in Swift with code only
How to write good code
[Swift] How to use UserDefaults
How to use Swift UIScrollView
How to migrate from JUnit4 to JUnit5
How to transition from the [Swift5] app to the iPhone settings screen
[iOS] [Objective-C] How to update a widget from an Objective-C app
[Swift5] How to avoid applying dark mode (dark appearance) in code
[Integration test code] How to select an element from date_select
How to apply C code format from the command line
How to push from Tarminal to GitHub
How to call classes and methods
[Swift] How to use SwiftLint (cocoapods)
[Swift] How to use Unwind segue
[Swift] How to send a notification
Switch from Eclipse to VS Code
How to write easy-to-understand code [Summary 3]
[Swift] How to replace multiple strings
[RSpec] How to write test code
Call a C function from Swift
How to change from HTML to Haml
How to open a script file from Ubuntu with VS code
[Swift5] How to communicate from ViewController to Model and pass a value
[swift5] How to specify color in hexadecimal
[Rails] How to convert from erb to haml
[IOS] How to get data from DynamoDB
[Swift] How to fix Label in UIPickerView
[Swift] How to use Tab Bar Controller
[Swift] How to implement the countdown function
[Flutter] How to use C / C ++ from Dart?
Java: How to send values from Servlet to Servlet
[Java] Flow from source code to execution
[Swift5] How to implement standby screen using'PKHUD'
[Swift5] How to create a splash screen
[Swift5] How to implement animation using "lottie-ios"
How to overwrite Firebase data in Swift
[Swift] How to use one option alert
How to count UTF-8 code points fast
How to return a value from Model to Controller using the [Swift5] protocol
[Swift] Summary of how to remove elements from an array (personal note)
How to call custom or standard services from Liferay 7 / DXP-Workflow / Workflow Script
[swift5] How to change the color of TabBar or the color of item of TabBar with code
[Ruby] How to convert from lowercase to uppercase and from uppercase to lowercase
Convert from C String pointer to Swift String type
How to link Rails6 Vue (from environment construction)
How to get a heapdump from a Docker container
How to dump from database (DB) to seeds file
[Swift] How to implement the LINE login function
Note how to use Swift super basic TableView
[swift5] How to transition from the app to an external site by specifying the URL
[swift5] How to implement the Twitter share function
How to add sound in the app (swift)
How to get Class from Element in Java
How to deploy
[Swift] How to link the app with Firebase
How to write code that thinks object-oriented Ruby
[Swift UI] How to disable ScrollsToTop of ScrollView
[Java] How to switch from open jdk to oracle jdk
[Swift] How to implement the fade-in / out function