Can I use Swift Package Manager in Obj-C code?

I was wondering if I could use SPM in a famous obj-c library and make it a swift package?

I am a complete greenhorn to swift development so pardon me if my question seems dumb.

Yes, unless there are other constraints you haven’t mentioned.

Some things that are common in Xcode frameworks, but which aren’t possible with SwiftPM yet include:

  • You cannot mix Objective C and Swift in the same target.
  • You cannot use resources.*
  • All your dependencies have to also support SwiftPM; and you cannot use binary libraries.*
  • You can only directly compile; you cannot make use of build phase scripts.

*The two with asterisks are already possible with development snapshots, but are too new to have seen inclusion in a stable release yet.

1 Like

Thanks for your answer. To be precise, I wanted to convert this library https://github.com/shpakovski/MASShortcut into a swift package so that I could use it in my swift project. Can I do it?

There are instructions to use this as a pod and do some changes if one wishes to use it in swift project but the link to the instructions is broken in the readme file.

I just scanned it quickly, but I don’t see mixed sources or external dependencies.

If there are any development scripts, you can always run them and check in the result.

You won’t be able to include the localized strings until SwiftPM support for resources comes out. But the keys look like proper English, so except that you would never see the other languages, it would probably behave normally.

Otherwise it looks like it should be possible. The only way to find out for sure is to try.

1 Like

I added the Package.swift file like this: https://github.com/rampatra/MASShortcut/blob/master/Package.swift and I successfully imported it into my swift project. However, when I try to run it, I get the below errors:

Any ideas?

When it’s a flavour of C, you have to specify header locations and such. See here.

Also, I don’t see any imports at the top of the file in the screenshot. This might come into play here.

Sorry but everything seems alien to me :( If you could please tell what exact changes I need to do in the library then I can try that or may be point me to a specific solution.

But thanks a lot for your time helping me.

  1. Create a directory Framework/include and symlink or copy these headers into it:

    Capture d’écran 2020-04-17 à 16 h 35 min 34 s

    This is only necessary because the headers import each other in such a way that they assume they will have all been copied into the same directory before they are actually used.

  2. Add this Package.swift:

// swift-tools-version:5.2

import PackageDescription

let package = Package(
  name: "MASShortcut",
  products: [
    .library(name: "MASShortcut", targets: ["MASShortcut"])
  ],
  targets: [
    .target(
      name: "MASShortcut",
      path: "Framework",
      exclude: [
        "Model/MASShortcutTests.m",
        "Monitoring/MASHotKeyTests.m",
        "Monitoring/MASShortcutMonitorTests.m",
        "User Defaults Storage/MASDictionaryTransformerTests.m",
        "User Defaults Storage/MASShortcutBinderTests.m"
      ],
      cSettings: [
        .headerSearchPath("UI"),
      ]
    )
  ]
)
  1. Add #import <AppKit/AppKit.h> to the top of any file that still does not compile. (Xcode was doing this automatically with the .pch file).

These instructions are enough to build and import. (They do not set up any test target or do anything with the resources.)

1 Like

Thanks a million for such an expounded answer. With the help of this, I am now able to successfully compile the library and run my project. I had to add #import <AppKit/AppKit.h> to all the files as most of the files were throwing errors while compiling.

Having said that, the library didn't work as expected. The custom key shortcut view in the library behaved weirdly and I have no clue why. Let me share the link to this page to the library maintainers and see what they have to say.

If in case, you want to see what changes I did based on your answer then you can have a look here: https://github.com/rampatra/MASShortcut

It looks like you removed a test file too. Was that related to wrapping it in a package, or for some other reason?

The XXX string you mentioned in the issue is comming from here. It looks like it is being used to detect when localization failed (by displaying it instead of falling back to the dictionary key). I suspect changing this line to pass key instead as a workaround will allow you to use the unlocalized English keys directly until resources can be done with SwiftPM.

1 Like

Ah, I see. I can try that. I think this might be it.

Also, there was another issue. The Masshortcut view distorted all other views on the view controller. It doesn't happen if I import via cocoapods but it does happen if I do it via SPM. So, I have to investigate that and then I think I would be able to finally use the library.

Thanks once again. You're a genius.

Sorry to butt in here, but is there any way to get something like this to work without an include folder? I really, really don't want to pollute repos with that just to get SPM to work. And would there be any difference for tools version 5.0 or 5.1?

How are you handling Objective-C/C header files now?

I'm trying to set the publicHeadersPath, but it doesn't seem to work. Consuming the package says there's no module and so nothing is imported. You can see it here.

Setting publicHeadersPath works for most projects. It’s relative to the target directory though, so it looks like the package at your link should be specifying "", not "AFNetworking".

When you don’t specify publicHeadersPath at all, it defaults to include, which is the only reason I chose that location for the header symlinks. For MASShortcut it was more complicated, because the headers were including each other at relative paths that weren’t valid yet from a raw checkout. The project expected the build system to copy them from various subdirectories into a single flat install location before actually trying to put them to use.

2 Likes

Thanks, that seems to work!

I understand the issue here and also the solution that would likely solve this issue. I have to use the English translation only as resources can't be packaged in SwiftPM now.

However, how do I actually implement the solution? How do I specify in code to pick the English translations?

P.S: I do not know Objective C, I am just a noob Swift programmer.

Literally just replace MASPlaceholderLocalizationString with key in the line I linked:

return [localizationBundle localizedStringForKey:key
  value:key
  table:MASLocalizationTableName];

This technically doesn’t use the English translation, but rather uses the raw key (instead of "XXX") if the translation table is unavailable. However, for the project in question, the difference is moot, because the keys all appear to be identical to their English translations.

1 Like

Great, thanks a lot :pray:

Terms of Service

Privacy Policy

Cookie Policy