How Do I Create a Development Framework In iOS Including Swift?
Asked Answered
L

2

7

My goal in this was to create an iOS framework that incorporates both Swift and Objective-C that I could use in my development projects. The nature of this framework is that the framework itself is undergoing development. So, it was important that each time I build a project using this framework (I'll call projects using the framework "using" projects for lack of a better term), I wanted to make sure the framework itself was re-built. I wanted this to be a framework because I have a few using apps across which I want to use the same framework code. I have struggled with this for a good hunk of today, and wasted a lot of time on something that should have been, in my thoughts at least, easier. So, I'll share my process.

Leavings answered 28/9, 2015 at 5:57 Comment(0)
L
11

The first thing to observe (which was certainly not my first observation!) is that you cannot do this using a static library under iOS. Xcode will not let you use Swift in a static framework Try it. Xcode will deny your wishes!

Here's the process I ended up with. The two main issues I had to deal with were: (i) making Xcode link to the framework in the using project without errors, and (ii) getting access to the headers of the framework in the using project. In Apple's enlightened view these two issues are separate. Note the sarcasm. ;).

1) Create a Cocoa Touch Framework using Xcode. I believe this works with Xcode6 and Xcode7. Use:

File > New > Project > iOS > Framework & Library > Cocoa Touch Framework

I happen to be using Xcode7. (Do not make a Cocoa Touch Static Library-- like I said above, Xcode will not let you incorporate Swift into static libraries).

2) With your Swift classes, make sure the members and functions are public. I've not experimented with this, but it seems that the public attribute is necessary for the members and functions to be visible to users of the framework.

3) Add what ever Swift classes (and Objective-C) you want to your framework.

4) Close that framework project. (The same project can't be open twice in Xcode, and you need to incorporate the framework into your using project next).

5) Open your using project in Xcode. For me this was an existing universal app project. You may be creating a new using project. In any event, drag the .xcodeproj file of your framework project, in the Finder, into your using project.

6) Inside of your using project, open your framework project. And drag the framework file into Embed Frameworks in Build Phases (the Embed Frameworks section wasn't present in Build Phases when I first started my experiments and I don't know yet what magic caused it to appear!).

enter image description here

These steps so far should enable you to build and link without actually yet integrating the usage of your library code. (I was using https://github.com/RadiusNetworks/swift-framework-example for some of my testing).

7) Now for the coup de grace: Under Build Settings, search for Framework Search Paths. And add in:

${TARGET_BUILD_DIR}/YourFrameworkName.framework

(It seems you do not have to have this marked as recursive).

8) In your Swift code files using the framework, you need to add an import at the top of each file:

import YourFrameworkName

You should now be able build and link using your new library!

9) One more gotcha: Make sure your Deployment Target for your framework matches your destination project. E.g., if your using project builds for iOS7, make sure your framework builds for iOS7 or earlier.

10) Second gotcha (10/23/15): I just learned that it is necessary for my framework to have "App-Swift.h" (the name I use for this) as the Objective-C Generated Interface Header name in Build Settings. When I took this (Objective-C Generated Interface Header) out (trying to fix another issue), I get serveral interesting issues coming up in App-Swift.h. These issues look something like: "Cannot find interface declaration for NSObject"?

11) Third gotcha (10/29/15): When I tried to upload my first app to iTunes Connect that makes use of this Framework, I got an uploading error. The error read:

ERROR ITMS-90206: "Invalid Bundle. The bundle at 'Your.app/Frameworks/YourFramework.framework' contains disallowed file 'Frameworks'."

Various SO and other posts have run into this kind of error, and the trick for me was, for the Framework target, in Build Settings, to set "Embedded Content Contains Swift Code" to NO. (My app Build Settings had this flag set to NO already).

An example project with most of these steps completed is on https://github.com/crspybits/CocoaTouchFramework.git

Leavings answered 28/9, 2015 at 6:36 Comment(2)
is not it unsafe to give total xocdeproject of the framework to other who wants to use your framework? does not it make your app open to all?Colossus
What do you mean by "make your app open to all"? As I mentioned, this writeup is in the context of my own development projects. I've not been distributing my frameworks to others where some kind of security or closed-source issues where at stake.Leavings
C
3

Swift consumer -> Swift dynamic framework

Xcode version 10.2.1

Create Swift framework

Create a framework project or create a framework target

File -> New -> Project... -> Cocoa Touch Framework
//or
Project editor -> Add a Target -> Cocoa Touch Framework

Two files will be generated:

  1. Info.plist - Build Settings -> Info.plist File. From Xcode v13 Info.plist is not presented as a file. To edit properties use Info tab
  2. <product_name>.h - Build Phases -> Headers. It is umbrella header file [About]

Add files .swift

Select `.swift` file -> Select File Inspectors Tab -> Target Membership -> Select the target
//or
Project editor -> select a target -> Build Phases -> Compile Sources -> add files

Build library - ⌘ Command + B or Product -> Build

Note: Be sure that you build the framework for the same process architecture as the client code.

Find generated output[Build location]

Products group -> <product_name>.framework -> Show in Finder

The framework includes

  • Info.plist
  • Modules[About] folder with:
    • module.modulemap
    • <product_name>.swiftmodule
      • .swiftdoc
      • .swiftmodule
  • Headers folder with:
    • files from Headers section. There are public interfaces/definitions
    • <product_name>-Swift.h - Xcode-generated header file[About]

Swift consumer with Swift framework

Drag and drop[About] the binary into the Xcode project

Embed dynamic binary(or not embed || link a static binary)[Link vs Embed] [Library not loaded]

//Xcode 11
Project editor -> select a target -> General -> Frameworks, Libraries, and Embedded Content -> path to `<product_name>.framework` -> Embed

//pre Xcode 11
Project editor -> select a target -> General -> Embedded Binaries -> path to `<product_name>.framework`

Add Framework Search paths(FRAMEWORK_SEARCH_PATHS)[Module not found] [Recursive path]

Project editor -> select a target -> Build Settings -> Search Paths -> Framework Search paths -> add path to the parent of `<product_name>.framework` file

Import module to the Swift client code[module_name]

import module_name

dynamic framework is a part of current workspace or project

In case when dynamic framework is a part of current workspace or project for FRAMEWORK_SEARCH_PATHS you can:

  • omit this step
  • use ${BUILD_DIR} or ${TARGET_BUILD_DIR} with recursive

In case when you clean the project you can get next errors:

Header 'module_name-Swift.h' not found

Could not build Objective-C module 'module_name'

In my case(shared framework for App and Extenssion) it was cased(in my opinion) by parallel build in Schema -> Build Order -> Dependency Order. As a workaround I used Manual Order(Deprecated) and:

  • When framework is a part of the same project. Place the framework at a first place(before extension) in App Target -> Build Phases -> Target Dependencies
  • When framework is a part of the same workspace. Edit Scheme -> Build -> Add framework target at first plase

Limitations:

  • Make sure that deployment target for framework is the same as for consumer or get

Compiling for iOS 14.0, but module 'module_name' has a minimum deployment target of iOS 16.4: path/arch.swiftmodule

[More examples here]

Countermark answered 6/12, 2019 at 15:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.