Xcode Swift: how to import a Swift project - ios

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)

Related

How to create SDK for iOS in Swift?

I have my own Xcode project which contains some controllers. I want to make its SDK, for use it in another application. In parent application it works as child. Parent app will share some data with my controller and my controller works on it and gives back some result. So kindly guide for me. Examples are - Payment Gateway SDK's. I am looking for the same.
I can see you add tag for swift. In Swift, static libraries are not supported, so you must exclusively use a framework (aka dynamic library) when linking an app to a library. Framework is similar to sdk.
Here are the steps:
1)Create the Framework using New > Project under IOS > Framework & Library, select Cocoa Touch Framework
2)To avoid the "ld: warning: directory not found for option..." goto Library Search Paths in Build Settings for your target and delete the paths.
3)You can't mix Objective-C with Swift so don't even consider adding the Swift-Header bridge file in your code.
4)There are some cases in swift where you need to import code from unexposed Frameworks. I've successfully used the module-map inside the framework to deal with these case.
5)I also select CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES in the Build Settings to solve 'include of non-modular header inside framework module'. That seems to work
6)I make sure that the header file that gets generated is marked as Public (not Project). Click on the file and you'll see the selection in the inspector under 'Target Membership'
Link
Also you can follow this tutorial. Since you have already created project.
Create a framework target, make all source code files a member of the new target, add the new target as target to the podfile. For everything else the tutorial should work. It should help you in understanding the steps.
Build framework

Xcode import my framework into a project

I've created an objC dynamic framework and I'd like to add it as a dependency into an objC project.
I've read those guides and some SO answers but none of them works:
https://developer.apple.com/library/content/technotes/tn2435/_index.html
http://netsplit.com/xcode-two-apps-with-a-shared-private-framework-in-a-workspace
https://medium.com/swiftworld/my-xcode-project-structure-for-open-source-project-1d363ff48534
https://www.raywenderlich.com/126365/ios-frameworks-tutorial
Basically what I do is open the host app and drag the framework project in it.
Then I drag the framework from the Products group of the framework project into the Embedded binaries section of the sample app.
What I see in the build phases is that I have:
In the target dependencies I can see the framework
In link binary with libraries I can see the framework
It has been added a new phase called Embed framework and my framework is there
If I try to import the framework module the compiler says
Module XXX not found
The only way to make it work seems to add in the framework search path of the build phases, the direct path to the product of the framework itself.
For what I understood it seems that somehow the path of the framework is not taken into account, but as far as I know in none of the guide is written to change it.
[NOTE]: no cocoapods or Carthage solution
Turns out that I had a wrong build settings in Per-configuration Build Products Path, instead of having it to point to build/debug-iphoneos it was pointing just to build .
Thus the process explained in the question works.
Hope this will help someone else.

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

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.
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!).
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
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:
Info.plist - Build Settings -> Info.plist File
<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
More examples here

How to reuse Swift code in other projects?

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.

How to use a static library (e.g. cocoapods library) on a XCTest?

I'm working with Core Data and, as I the model gets more complex, I need to make sure that the new changes I introduce don't break my model unexpectedly in other parts.
I can create unit tests and run them every time I change something on my model. If something breaks, there might be something wrong with my model or at least I know I have to modify some queries in the main code/tests.
I'm using MagicalRecord to have access to some convenience methods.
I also use cocoapods for the same reason, convenience.
The problem is that cocoapods creates a static library and links it against my target, but in Xcode, new test targets are not automatically configured to link against the same libraries/frameworks the target in question links against to.
How can I have a XCTest link against a static library?
This is not only helpful with MagicalRecord/Core Data, but when you're using an external library it's a good idea to have tests to make sure that updates on the library don't break your App.
If you're using cocoapods, you can simply use link_with to include your test target, but if you're using a static library not created by cocoapods you can do the following:
(I will still use a cocoapods library for the instructions, as that's what I'm working with, but the idea is the same if you're not using a cocoapods library)
Once you have created a new Test Target, click on the project root node in the project navigator and select your test target.
Go to Build Settings and search for Header Search Paths. Double click on the Header Search Paths item and enter
${SRCROOT}/Pods/Headers and select recursive if you want to import all of your cocoapods libraries headers or enter them individually:
${SRCROOT}/Pods/Headers/MagicalRecord leaving non-recursive selected (although in this case it doesn't really matter).
Now search for Linking and in Other Linker Flags add -ObjC
Now with your Test Target still selected, go to Build Phases and in Link Binary With Libraries click on the + and add libPods.a or the other libraries individually (libPods-MagicalRecord.a)
You should be able to run a XCTest using the static library.
Optional: I like to import the headers I know I'm going to use in the -Prefix.pch file. You can go to your target test group in the Project Navigator. Go to the Supporting Files group and open the -Prefix.pch file. For MagicalRecord I like to add:
#define MR_SHORTHAND
#import "CoreData+MagicalRecord.h"
For more information:
Unit Testing with Core Data
Creating a Static Library in iOS
Tutorial
After a lot of fighting, these steps worked for me:
1) Project> Info
On configurations, set the Test Target to share the same Configuration File as your main project (generated by Cocoapods).
Now, you should start to get some errors because XCUnit framework is missing, but now your external libraries imported with CocoaPod are visible on your test project.
2) On the Test Target>Build Settings look for Header Search Paths, once there add:
$(DEVELOPER_DIR)/Platforms/iPhoneOS.platform/Developer/Library/Frameworks
$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks
The Unit Test framework is inside your Xcode App, this headers will make them public to be added later.
3) On the Test Target> Build Phases add the SenTestingKit.framework
And it should look like this
From there, everything seems to work for me. Good Luck.

Resources