{"id":7012,"date":"2014-06-26T13:30:08","date_gmt":"2014-06-26T10:30:08","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=7012"},"modified":"2021-08-11T18:38:32","modified_gmt":"2021-08-11T15:38:32","slug":"creation-of-pure-swift-module","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/creation-of-pure-swift-module\/","title":{"rendered":"Creation of pure Swift module"},"content":{"rendered":"\n<p>If you have already started playing with swift, you probably thought about how to include third party libraries into your project or how to distribute yours.<\/p>\n\n\n\n<p>Apple provides a mechanism to distribute code via frameworks (eventually, for iOS too), so making a custom framework, which will include both ObjC and Swift code is very easy. But let&#8217;s dig deeper and create a <strong>pure<\/strong> Swift module, like apple does with Swift&#8217; std lib and Cocoa\/CocoaTouch bridge.<\/p>\n\n\n\n<p><em><strong>Note:<\/strong> this module will work in swift-only projects, in case if ObjC compiler generates Swift-to-ObjC bridging header and includes swift-module via <code>@import<\/code> directive, which doesn&#8217;t work with current Xcode\/Apple Clang version.<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Toy Swift Module<\/h3>\n\n\n\n<p>We&#8217;re going to create a simple module call <code>Logger<\/code> which will contain only one method: <code>log<\/code>.<\/p>\n\n\n\n<p>You can see sample project <a title=\"ToySwiftModule\" href=\"https:\/\/github.com\/AlexDenisov\/ToySwiftModule\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n\n\n\n<p>Each swift module consists of at least three files, so we should get all of them as an output:<\/p>\n\n\n\n<p><code>Logger.swiftmodule<\/code> &#8211; public interface\/definitions<\/p>\n\n\n\n<p><code>Logger.swiftdoc<\/code> &#8211; docs (surprisingly)<\/p>\n\n\n\n<p><code>libLogger.a<\/code> &#8211; built library (there also might be a <code>dylib<\/code>, it depends on your task)<\/p>\n\n\n\n<p><em><strong>Note:<\/strong> you should switch <code>xcrun<\/code> to beta xcode&#8217; version before running <code>swift<\/code> commands<\/em><\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:2 lang:sh decode:true\">sudo xcode-select -switch $xcode_dir\/Contents\/Developer<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">libLogger.a<\/h3>\n\n\n\n<p>Let&#8217;s create the simplest and useless <code>Logger<\/code> &#8220;library&#8221;.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:swift decode:true\">class Logger\n{\n    var prefix: String\n    \n    init(_ prefix: String) \n    {\n        self.prefix = prefix\n    }\n    \n    func log&lt;T&gt;(object: T)\n    {\n        print(prefix)\n        println(object)\n    }\n}<\/pre>\n\n\n\n<p>The class just takes some prefix and logs it before actual object<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:swift decode:true\">var logger = Logger(\"&gt; \")\nlogger.log(\"Hello, World!\")<\/pre>\n\n\n\n<p>Now it&#8217;s time to make a <code>libLogger.a<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:2 lang:sh decode:true\">xcrun swift -emit-library -emit-object Logger.swift -sdk $(xcrun --show-sdk-path --sdk macosx) -module-name Logger\nar rcs libLogger.a Logger.o<\/pre>\n\n\n\n<p><code>-emit-library<\/code> generates dynamically linked shared library, while <code>-emit-object<\/code> generates object file <strong>and includes <code>main<\/code> function<\/strong>, so you will have linker errors due to duplicated symbols.<\/p>\n\n\n\n<p>Solution is pretty simple: include both flags <code>-emit-object<\/code> and <code>-emit-library<\/code>, as I did above.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Logger.swiftmodule<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:2 lang:sh decode:true\">xcrun swift -emit-module Logger.swift -sdk $(xcrun --show-sdk-path --sdk macosx) -module-name Logger<\/pre>\n\n\n\n<p>This command will generate <code>Logger.swiftdoc<\/code> and <code>Logger.swiftmodule<\/code>.<\/p>\n\n\n\n<p>Now we have complete module and can integrate it into real project. Just create simple swift-project and add the files:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/swift_module_integration.png\"><img loading=\"lazy\" decoding=\"async\" width=\"257\" height=\"170\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/swift_module_integration.png\" alt=\"swift_module_integration\" class=\"wp-image-7015\"\/><\/a><\/figure>\n\n\n\n<p>Then setup &#8216;Import paths&#8217; for Swift<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/swift_import_paths.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1014\" height=\"362\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/swift_import_paths.png\" alt=\"swift_import_paths\" class=\"wp-image-7016\"\/><\/a><\/figure>\n\n\n\n<p>We&#8217;re ready to check how it works:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:swift decode:true\">import Foundation\nimport Logger\n\nvar logger = Logger(\"&gt; \")\nlogger.log(\"Hello\")<\/pre>\n\n\n\n<p>Just run and you&#8217;ll see an expected output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:2 lang:sh decode:true\">&gt; Hello<\/pre>\n\n\n\n<p>If you see linker errors, check your &#8216;Library search paths&#8217; and &#8216;Other linker flags&#8217; &#8212; they should contain path to <code>libLogger.a<\/code> and <code>-lLogger<\/code> respectively.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Logger.swiftdoc<\/h3>\n\n\n\n<p>Let&#8217;s figure out how to deal with documentation.<\/p>\n\n\n\n<p>To add documentation to module you just need to comment it using <code>\/\/\/<\/code>, e.g.:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:swift decode:true\">\/\/\/ Simple Logger\n\/\/\/\n\/\/\/ Constructor takes prefix string which will be printed before actual object\n\/\/\/\nclass Logger\n{\n    var prefix: String\n    \n    init(_ prefix: String)\n    {\n        self.prefix = prefix\n    }\n    \n    \/\/\/ Prints `object` with specified `prefix`\n    func log&lt;T&gt;(object: T)\n    {\n        print(prefix)\n        println(object)\n    }\n}<\/pre>\n\n\n\n<p>After integrating module into a project you will see documentation on the right<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-25-at-17.14.58.png\"><img loading=\"lazy\" decoding=\"async\" width=\"807\" height=\"234\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-25-at-17.14.58.png\" alt=\"Swift module documentation\" class=\"wp-image-7017\"\/><\/a><\/figure>\n\n\n\n<p>But I didn&#8217;t manage to get it work without restarting Xcode after integrating.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>This approach is not very nice for &#8220;everyday&#8221; usage for a regular iOS\/OSX developer (because it requires creating and supporting Make\/CMake file), but it might be useful if you want to create pure module which doesn&#8217;t use ObjC at all.<\/p>\n\n\n\n<p>Also, Swift modules are similar to Java&#8217;s <code>jar<\/code> binaries.<\/p>\n\n\n\n<p>The source code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:swift decode:true\">import Foundation\n\n\/\/\/ Simple Logger\n\/\/\/\n\/\/\/ Constructor takes prefix string which will be printed before actual object\n\/\/\/\nclass Logger\n{\n    var prefix: String\n    \n    init(_ prefix: String)\n    {\n        self.prefix = prefix\n    }\n    \n    \/\/\/ Prints `object` with prefix\n    func log&lt;T&gt;(object: T)\n    {\n        print(prefix)\n        println(object)\n    }\n}<\/pre>\n\n\n\n<p>transforms into module interface without details of implementation<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:css decode:true\">\/\/\/ Simple Logger\n\/\/\/\n\/\/\/ Constructor takes prefix string which will be printed before actual object\n\/\/\/\nclass Logger {\n    var prefix: String\n    init(_ prefix: String)\n\n    \/\/\/ Prints `object` with prefix\n    func log&lt;T&gt;(object: T)\n}<\/pre>\n\n\n\n<p>So you can even distribute proprietary libraries without any problems (except of crazy reverse engineers), but I hope you won&#8217;t do this in favor of Open Source Software ;)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you have already started playing with swift, you probably thought about how to include third party libraries into your project or how to distribute yours. Apple provides a mechanism to distribute code via frameworks (eventually, for iOS too), so making a custom framework, which will include both ObjC and Swift code is very easy&#8230;.<\/p>\n","protected":false},"author":55,"featured_media":9449,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["AlexDenisov"],"class_list":["post-7012","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/06\/swift_module_integration.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/7012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/55"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=7012"}],"version-history":[{"count":20,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/7012\/revisions"}],"predecessor-version":[{"id":13936,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/7012\/revisions\/13936"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9449"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=7012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=7012"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=7012"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=7012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}