How to reuse Swift code in other projects? - ios

I wrote a class in Swift. I want to use this code in two separate iOS app projects that I wrote. Both the shared code and the apps are written in Swift. What is the best way of doing that?
I tried to create both a framework and a library in Swift and then add it as a sub-project to my app. In both cases I could not make the app see the module. I tried to add the shared module to "Target Dependencies" and "Link Binary With Libraries" of the main app's target. No luck - the app still can not see the classes of the shared module.
Using Xcode6 Beta6 at the moment.
Solution
As Konstantin Koval and Heliem pointed out, all I needed is to use public in my class and method declarations, in the shared module. Now all works, both if I use workspace and if I add the module as a subproject.
Update
I just found an excellent easy solution for reusing code between projects in Swift. It is called Carthage (https://github.com/Carthage/Carthage). This is not a plug as I am not affiliated with it in any way, I just really like it. It works in iOS 8+.

Create a new project (iOS Cocoa Touch Framework) for your reusable code
Move your classes to that Framework project
Mark your methods and classes, that should be visible to others as public
Create Workspace.
You can create a workspace on step 1. When you create new Framework project, Xcode will ask you if you want to create new workspace and add that project to workspace. This is the best approach
Add both your project and Framework to the workspace
Select you project target -> General tab. Add Framework and Libraries (add your library here)
When you want to use code from your Library in swift file, import it using import 'LibTargetName'

You can take a more programatic approach by using SWM (Swift Modules): https://github.com/jankuca/swm
It is very similar to npm (node.js package manager) or bower (client-side module manager); you declare your dependencies in a swiftmodule.json file like below. It does not have a central module registry like the two mentioned JS solutions, it only accepts git URLs.
{
"name": "ProjectName",
"dependencies": {
"Dependency": "git://github.com/…/….git"
}
}
…run swm install and have the Dependency module (compiled into a *.swiftmodule binary) ready for import in the .modules/ directory.
import Dependency
And if you prefer to skip Xcode for app development, you can also build your whole app using swm build (more info in the project's readme).
The project is still in early stages but it makes itself useful a lot for me at least. It provides the most clean (and clear) way of creating importable modules.

Here is a video which is very straightforward: http://eonil-observatory.tumblr.com/post/117205738262/a-proper-way-to-add-a-subproject-to-another-xcode
The video is for OS X instead of iOS. But you will get it and figure out the process for iOS.
Let's assume that AppA needs to reused code from SharedProject.
The following process works for me in Xcode 7 Beta:
Create SharedProject. Project type must be Framework instead of Application. You write common code in this project and mark the code as public.
Open AppA in Xcode, open the folder which contains SharedProject in Finder. Drag the .xcodeproj file of SharedProject from Finder and drop it into the root folder of AppA in Xcode Project Navigator.
AppA --> Build Phases --> Link Binary with Libraries. Add SharedProject.
import SharedProject and reuse code from SharedProject!
Edit:
Nowadays I suggest you use Carthage. It's better than the home made solution above.

Related

Xcode Swift: how to import a Swift project

I have made an Xcode Swift project ("Project1"). In a new project ("Project2"), I have trouble adding project 1.
I have tried adding project 1 to project 2's build phases (target dependancies, compiled sources, link binary with libraries); didn't work. When adding to the compiled sources, it wouldn't work no matter which option I chose (folder references, groups, copy if needed).
I get no compiler errors at:
import Project1
But when I try to use a class from project 1, I get the error "Use of undeclared type".
I have also tried to following links with no success:
External library usage in xcode
Xcode : Adding a project as a build dependency
Xcode how to add an external project
Both projects are in Swift (iOS).
I'd be very thankful if someone helped me with this issue.
Update: Project 1 is not a framework - it's an iOS app. I need to use some of its classes in project 2. The problem is that project 1 uses the Objective C library Common Crypto via a bridging header. When I manually add project 1 classes into project 2, I get an error ("unresolved identifier") in the project 1 Swift code that uses Common Crypto.
So in a nutshell: I have an iOS app (project 1), which is in Swift but uses Common Crypto via bridging header. When I add a number of classes from project 1 into project 2, it cannot resolve the references (in project 1) to Common Crypto variables.
Assuming Project1is a Framework and Project2 is an application using the framework:
Create a virgin Workspace (Xcode File -> new -> Workspace) named TestWorkspace
From the Finder, drag the Project1.xcodeprojfile to the TestWorkspace
From the Finder, drag the Project2.xcodeprojfile to the TestWorkspace, above Project1
Edit your TestWorkspace schemas Build setup:
Add Project1 and Project2
make sure Project1 is above Project2
Untick "Paralellize Build" to assure Project1 is build first
Build
Select Project2s target -> General
Drag artefact project1.framework(in Productsgroup) to "Linked Framworks and Libraries"
Note: To be visible for the client, all classes and methods in your project1.framework have to be public or open. Finde detailed information in Apples documentation.
Edit: As you have CommonCrypto as a dependency you will have to add the module to your Project2 project instead to solve your issues ( this is the easiest without resorting to an umbrella framework ). Add a run script build phase and include http://pastebin.com/1vmiqffu
-- Credits: Script 'stolen' from: https://github.com/henrinormak/Heimdall
Ok so I'm going to assume here that Project1 actually has a framework as a target. What are the access permissions set on the types you are trying to use?
Here are a couple of catchya's with Swift and frameworks as I encountered them:
You do not have a bridging header, instead your framework includes headers of non-Swift dependencies inside the header file of your framework ( ModuleName.h ). This also means these will be available to whatever project you import them to. As far as I know you need to use a module.modulemap in order to make use of private headers and includes.
All Swift Classes / Structs / Definitions in general are internal by default. It is a very good design choice and it forces you to think about the access rights on every component you write. Keeping things private by default makes it easier to only open stuff that really needs to be open ( public, open ), allowing for easier code maintenance since you know that private things are only accessed within the same context. ( Otherwise: error )
For some more assistance this link might be of help to you on how to do some fundamentals:
your first ios framewok (swift)

Is it possible to create a standalone distributed Swift Framework?

I have a scenario where I need to package some common code into a Swift only framework. I have found many tutorials on how to do this but all involve creating a framework within an iOS app. My goal is to:
Create a seperate Framework project
Build and produce a .framework file
Copy that single file(without all the source files) into some iOS app project, add it under "Linked Libraries" and use it.
Question: Is this possible or do I have to include all of the Classes that are part of the framework?
I have added a framework project to an iOS app and had success using it because all the source code was in the Framework project. I tried to copy that .framework file alone to a new project and use it but when trying to access the classes or instantiate the contents of the framework I couldn't even-though whatever needed to be declared as public was.
Appreciate any help...

How to create an Xcode project that is not an app but is visible in other projects?

I want to create a project with a handful of categories that add useful functionality to UIKit.
I want to keep this as a separate project so that it can be source-controlled separately (and eventually hosted on github, and downloadable as a dependency via cocoa pods...but let's skip this for now.)
But all the Xcode 6.1 project templates imply an app inside. When I select an empty project it complains about missing base SDK. But this cannot be set without a target present. It is quite confusing. I don't need a target. Do I?
The project should be just a shell for a number of categories + a accompanying summary header file.
I want to be able to drag this project as a subproject to any other proper app project, import the summary header file and expose those categories to the main project.
Any advice?
You need a target. Every source file that should be compiled must be a part of a target.
You could reference each source file from the target(s) in your app's Xcode projects, but that'd be tedious as you'd have to add to every project manually when you add source to your shared repository.
Either create an embedded framework project or a static library. Unless you are really going to share code between an app and an app's extension, go with the static library. It is a lot easier to manage.
Create a project that has a static library target, add the files to that static library.
Then create a workspace that has your static library project and your app(s) project(s) in it. Modify the app targets to link against the static library.

Swift: using private framework

I have built an iOS Swift framework with Xcode.
After writing the code I have build the project and took the .framework file inside the Products folder to test it.
To test the framework have open a new application and drag and drop the .framework file previously built and I added it into the embedded binaries list of my application project.
To import it into my ViewController.swift class I wrote:
import frameworkName
No problem until here, this means that the project sees the framework.
When I try to use a public class inside the framework with:
var x : className?
I get the following error:
'className' is unavailable: cannot find Swift declaration for this class
What does it mean? What is the problem?
When you're referencing a framework in the products directory from your workspace, ensure that the location is "Built Products" and the filename is just the framework name, without any additional path components.
If you're referencing a framework that isn't in your workspace, I would recommend using Carthage instead of copying it directly into your repository. This will make versioning much easier, and will ensure that it is built correctly for both simulator and device.
To actual a self defined framework, u really have to do a lot of things.
Firstly, make sure ur framework is used on right device. It's to say framework can only be used on corresponding device(either one of Simulator, Device and Mac). In other words, if framework A built on simulator, project import framework A can only pass compile and successfully built on simulator.
P.S. if universal version desired, -lipo command is what u need to further explore.
Secondly, during implementing ur framework , make sure all the classes, methods and variables u want use outside start with Public.
Thirdly, check ur project setting Embedded Binaries and linked Frameworks and Libraries do contain ur framework.

Don't we need to link framework to XCode project anymore?

Base on this question
Why don't iOS framework dependencies need to be explicitly linked to a static library
I read the selected answer and still don't understand so I made an example project
Test Project on Github
In the test project, I remove all framework from Link Binary With Libraries and File navigation for both main project and the static library (including Foundation.framework and UIKit.framework too), basically, both project link to 0 frameworks.
Questions are
In static library, it's including MapKit/MapKit.h without referencing the Mapkit.framework to the project, why is its still working?
In main project, I remove UIKit.framework and Foundation.framework from the project, why is it still working?
Since it's working for now, will there be any issue later?
Thank you for your comment.
P.S. By working, I mean I can run on the simulator and I can archive the main project without any error.
Edit 25/07/2014
I tried with the real app that I'm working on, it's the same.
I highlight Foundation, UIKit, CoreData and 10 another frameworks in File Navigation, well, all of them.
Uncheck the target in Utilities Panel --> Target Membership
Build : Pass, Run : Pass
Every functionality of my app is still working as expected. I don't get this.
Check your project build settings. Underneath LLVM 5.1 — Language — Modules you should see the option 'Link Frameworks Automatically'. In your case it sounds like it's set to 'YES', the default.
In that case, instead of producing an error when you reference a class that the compiler doesn't know, it'll figure out which Framework contains that class and link it. In your code it'll be MKMapView or one of the other MapKit classes that triggers the linkage.
EDIT: from the relevant 'What's New?' document:
Auto Linking is enabled for frameworks imported by code modules. When
a source file includes a header from a framework that supports
modules, the compiler generates extra information in the object file
to automatically link in that framework. The result is that, in most
cases, you will not need to specify a separate list of the frameworks
to link with your target when you use a framework API that supports
modules.
Another way of looking at it is that the compiler is smart enough to mutate #import to #import when the framework has been built appropriately. All system frameworks have been.
To elaborate #Tommy's answer, a framework that supports modules satisfies the following 2 conditions:
Under Build Settings > Packaging
Define Modules is set to YES
Module Map File exists.
So, if you're certain that the framework you're using in your code modularizes like that, you can choose to not explicitly add it in the link phase as it will be automatically added as long as in the project file, under Apple Clang - Language - Modules, The option Link Frameworks Automatically is set to YES.

Resources