visit our site

Creation of pure Swift module

In Development, iOS, Mac OSX, Swift

by AlexDenisov by June 26, 2014

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. But let’s dig deeper and create a pure Swift module, like apple does with Swift’ std lib and Cocoa/CocoaTouch bridge.

Note: this module will work in swift-only projects, in case if ObjC compiler generates Swift-to-ObjC bridging header and includes swift-module via @import directive, which doesn’t work with current Xcode/Apple Clang version.

Toy Swift Module

We’re going to create a simple module call Logger which will contain only one method: log.

You can see sample project here.

Each swift module consists of at least three files, so we should get all of them as an output:

Logger.swiftmodule – public interface/definitions

Logger.swiftdoc – docs (surprisingly)

libLogger.a – built library (there also might be a dylib, it depends on your task)

Note: you should switch xcrun to beta xcode’ version before running swift commands

libLogger.a

Let’s create the simplest and useless Logger “library”.

The class just takes some prefix and logs it before actual object

Now it’s time to make a libLogger.a:

-emit-library generates dynamically linked shared library, while -emit-object generates object file and includes main function, so you will have linker errors due to duplicated symbols.

Solution is pretty simple: include both flags -emit-object and -emit-library, as I did above.

Logger.swiftmodule

This command will generate Logger.swiftdoc and Logger.swiftmodule.

Now we have complete module and can integrate it into real project. Just create simple swift-project and add the files:

swift_module_integration

Then setup ‘Import paths’ for Swift

swift_import_paths

We’re ready to check how it works:

Just run and you’ll see an expected output:

If you see linker errors, check your ‘Library search paths’ and ‘Other linker flags’ — they should contain path to libLogger.a and -lLogger respectively.

Logger.swiftdoc

Let’s figure out how to deal with documentation.

To add documentation to module you just need to comment it using ///, e.g.:

After integrating module into a project you will see documentation on the right

Swift module documentation

But I didn’t manage to get it work without restarting Xcode after integrating.

Conclusion

This approach is not very nice for “everyday” 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’t use ObjC at all.

Also, Swift modules are similar to Java’s jar binaries.

The source code:

transforms into module interface without details of implementation

So you can even distribute proprietary libraries without any problems (except of crazy reverse engineers), but I hope you won’t do this in favor of Open Source Software ;)

* Railsware is a premium software development consulting company, focused on delivering great web and mobile applications. Learn more about us.
  • Pingback: One month of Swift » We ❤ Swift()

  • I can not get this to work on Beta 4. It can’t find the object reference. Any ideas? BTW: I am doing it all on command line while your example is using XCode for the reuse case.

    • Alex Denisov

      Which object reference do you mean? While making module or while integrating it?

      • While integrating it. Trying to use it in another program always says that the definition can not be found. Beta 4 adds public and private declarations, but there must be something I am not getting right about it.

        • Alex Denisov

          I haven’t tried this approach.
          Could you tell what exactly you doing, so I might help you?

          • I followed all the steps you did above. But instead of using XCode for the main.swift file, Icompiled the main.swift file with:

            xcrun swift -I “./” -L “./” -lLogger -sdk $(xcrun –show-sdk-path –sdk macosx) main.swift

            It returns:

            main.swift:1:14: error: use of unresolved identifier ‘Logger’

            var logger = Logger(“> “)
            ^

            What am I missing in the command line? I got it to work before Beta 4, but not now.

            • kc

              First, you need to make class and method public. Check Alex’s GIT. The screenshot here has not been updated.

              Second, I compile it first and execute it (not really running like a script).

              % xcrun swiftc -o main -I “./” -L “./” -lLogger -sdk $(xcrun –show-sdk-path –sdk macosx) main.swift

              % ./main

              Maybe there’s something better than doing this.

  • Vi

    Hi.

    Thanks for great post.

    I tried to follow steps, but got stuck on xcrun swift -emit-object. Terminal says that this argument is not available for swift command, so I switched to xcrun swiftc, which has such argument available.

    Afterwards, I got message “error: the SDK ‘MacOSX10.9.sdk’ does not support Swift”

    Any ideas on further steps?

    Thanks.

    • Yea, the latest version only works with OSX 10.10. Not sure why they made that limiting factor, but it is there. I have two systems: one with 10.9 and one with 10.10. It currently only compiles like this on the 10.10 system.

  • Vi

    It worked with sdk 10.10, great. Another question I have: any chances to include several classes into one library? Moreover, I was not able to create library from class, which is importing AVFoundation. Library provides possibility to play sound files (which are inside library). I want library to control all the AV flow, but not main project.
    Or this should be created as framework?

    Thanks

    • Coder

      I try this on 10.10 – its not work for me.

  • On the topic of modules, llvm have a pretty in-depth doc on modules and module map files.

    http://clang.llvm.org/docs/Modules.html

    It takes a while to get the hang of customising your own modules via mod maps, and also note that it’s probably best to compile everything from terminal rather than Xcode if you head down this route (though I’m not ruling Xcode out)…

    What I found particularly interesting is the bit entitled “Module Map Language” which mentions the assert.h from std. I’ll try experimenting later with getting some of the older Obj-C macro’s back into swift.

    Something else that this might be able to handle, if you want to have a “pure swift” module, is the ability to import some of the libraries which are particular to Obj-C only. The bsd.h and one that I miss is asl.h for logging (vsyslog just doesn’t do it for me)…

    Another avenue that I want to look at and hope to overcome is bridging the c and cpp libs via this route. However, I haven’t looked at this enough to even start to guess how easy (or involved) this is going to be.

    From what I’ve gathered so far, if you include external libraries to swift i.e. other languages, you might end up doing several compiles to get the desired result. I first noticed this when I watched the fs-events whilst I compiled a swift, Obj-c and C mingled project. It appears that and off the top of my head this order is correct:

    1) clang compiles the c-related libraries first and dumps them as .o and map (header-map) files (and if you include the flags, you can get the IR output which would be especially handy, see: http://clang.llvm.org/hacking.html)

    2) clang then starts on the obj-c content and does the same

    3) now swift (the binary in bin/ in the toolchain belt) kicks in, and what appears to pop out are modules (well partial modules), which I guess is each .h (each separate swift file produces it’s own .h persè)

    4) swiftc (also in bin/ folder) then kicks in and cleans up.

    Between all of this you get the usual ld kicking in and doing the usual.

    I’m definitely going to keep an eye on this blog, as from what I’ve seen, swift has ALOT of potential and if anything, I’d prefer to move away from the very [square] obj-c. (hah excuse the pun).

    Super blog post! very interesting read indeed!

    Cheers,

    AdriHD

  • i4niac

    Any chance this could be done for iOS? Trying -sdk iphoneos didn’t work for me, lots of compile errors. I’m looking for a way to build a static library to reuse in iOS 7.0 projects.

  • This is a cool article, but do you have any convenient way of doing this for more than only one swift file? I mean, writing the compile line for every file could be a real pain.

  • Eric

    I’ve tried this with XCode 6.1.1. I found that I had to replace “xcrun swift” with swiftc. With that change, I was able to build my .o files (I have more than one class).

    One problem I had, though, was that when I copy the .a, .swiftdoc, and .swiftmodule files into a new project and try to import my library, the import doesn’t work – XCode says there is no such module with that name. Maybe something has changed with XCode 6.1.1?

    Also, it might be worth mentioning that I have one class that depended on another, and if I tried to build them individually, I got a bunch of build errors on the class that depends on another. However, if I build them all in one command using *.swift, it is able to successfully generate all the .o files.

  • Pingback: Crash Course | swiftioscodetutorial()

  • Matthew Adams

    Just curious: why the leading underscore in the Logger class’s constructor? Why opt out of external names for parameters?


    init(_ prefix: String)

  • Pingback: Happy Birthday, Swift! - We ❤ Swift()

  • Pingback: My Homepage()

  • Pingback: Building pure Swift Cocoa Touch Framework - DexPage()

  • Pingback: How do I import a Swift file from another Swift file? - DexPage()

  • Pingback: http://xbl4free.net/()

  • Pingback: more info()

  • Adam Smith

    l receive an error with the example project and when creating myself: ‘module file was created by an older version of the compiler’ but I had code-select pointing towards Xcode-beta and I have created the project in Xcode 7

  • Andrea Terenziani

    How can I export the module to reuse it within other projects?

    • I assume the approach described is not working with the latest toolchain.
      It’s not supposed for production, just an example and attempt to understand how it works.

  • Renzo Crisóstomo

    Do you know what’s the equivalent of “Import Paths” for an Objective-C project? I tried all settings in Search Paths section on Build Settings but nothing worked, my Objective-C code can’t import the module generated by swift-build-tool.

  • Amr Ahmed Elghadban

    can you demonstrate via short video from scratch

    • I’m afraid the approach described above is outdated and doesn’t work anymore. Hence, there is no point to make a video.

      • AmrAngry

        am struggling to find a good example, if i find one will link it here , thanks a lot for fast replay

Signup for our weekly newsletter

Want to get more of Railsware blog?

RSS Feed

We're always ready to help!

Contact us