Create and import swift framework - iphone

I'm new in swift programming.
I need to create pure swift framework and import it in my existing pure swift project.
When I try to import swift framework, I'm getting this error
message:
"Could not build Objective-C module '<myModule>'"
Test.h
import Foundation
public class Test {
class func printTest() {
println("111");
}
}
Asdf.h
import UIKit
public class Asdf: Test {
class func echo() {
println(888);
}
}
myModule-Swift.h
#ifndef <myModule>_<myModule>_Swift_h
#define <myModule>_<myModule>_Swift_h
#endif
After framework build, i have added framework in my existing project and get this
What am I doing wrong? Thanks For Help!
#findall comment answer -
I tried to add all framework files to my project root folder and inside project folder, but again got the same error

I've done with the following steps.
Create a framework project, for example named "FooKit". (Cocoa Touch Framework to be selected)
Create a ".swift" file and add a public symbol, for example public func foo(), to it.
Create an use-side (app) project. (I've chosen Single View Application)
Open the root directory of "FooKit" by Finder, drag "FooKit.xcodeproj" there and drop it into the app project via Project Navigator.
Add "FooKit" to Target Dependencies in the app's Build Phases setting.
Add "FooKit.framework" to Embedded Binaries in the app's General setting.
Now you can build like this code in the use-side app.
import FooKit
func bar() {
foo()
}

In Xcode 7.2 using Swift 2.1 I managed to get past the mentioned error by making sure that the build settings of your pure Swift framework called Foo are as follows:
1) Build Settings->Packaging->Defines Module = Yes
2) Build Settings->Swift Compiler - Code Generation->Install Objective-C Compatibility Header = Yes (if you do not need to import your Swift framework into Objective C set this setting to No)
3) Erase the string value of Build Settings->Swift Compiler - Code Generation->Objective-C Bridging Header
4)Build Settings->Swift Compiler - Code Generation->Objective-C Generated Interface Header Name = Foo-Swift.h (if you do not need to import your Swift framework into Objective C erase this setting as you did in step 3))
5) Make sure that Build Settings->Packaging->Product Name = Foo
6) Add to the public umbrella header of your framework (Foo.h), which you can use to import your Swift code to Objective C, the following line:
#import "Foo-Swift.h"
(but if you do not need to import your Swift code into objective C skip this step)
6) Add the following line to the Swift file where you want to use the module Foo:
import Foo
Important notes:
1) Ensure that your umbrella header Foo.h is set to public in the File Inspector, otherwise this won't work.
2) If you are using your pure Swift framework in Objective C files, ensure that:
the Foo-Swift.h header has generated the correct interface by going to Foo.h and then clicking on the top left corner menu of the Xcode code editor and then choosing Includes->Foo-Swift.h (I had to rebuild the Foo framework a few times until Xcode caught-up with the change and then generated the correct interface in Objective C, so you may need to do that as well)
your swift classes inherit from NSObject or they won't be available to Objective C code

Additionally for #jvarela's answer, in Xcode 9 and swift 4,
To import Swift framework in an Objective C project, you must add
#objc
mark to all of your classes, methods and variables which you want to export into Objective C project .
For example,
#objc public class Foo: NSObject {
#objc public var fooDescription: String
#objc public func foo(){
}
}

On Swift:
Create Framework:-
Start Xcode -> Create a new Xcode Project -> iOS -> Framework & Library -> Cocoa Touch Framework -> Name the framework(ex. sampleCocoaFramework) -> Create.
Set Taget -> General -> Deployment info -> Deployment Target.
Add a public class: File -> New File -> iOS -> Swift File -> Name it (ex. openCocoaClass) -> Create.
Now add some code to the openCocoaClass.swift.
import Foundation
public class openCocoaClass {
public init() {
}
public var samplePublicVariable = "samplePublicVariable # openCocoaClass"
public func samplePublicFunction()
{
print("samplePublicFunction # openCocoaClass")
}
}
Clean the project : Product -> Clean
Configure the scheme settings : Product -> Scheme -> Edit Scheme -> Run -> Build Configuration -> Release.
Build the framework : Product -> Build.
Export the framework : Products -> Select the framework -> Identity and type -> Full Path -> Released Framework.
Adding Framework to Project:-
Start a Xcode project and name it (ex. CocoaFrameworkTest).
Drag and drop the sampleCocoaFramework.framework to the CocoaFrameworkTest's project folder.
Target -> General -> Embed Binaries -> Add Other -> Select Framework
-> Copy Items if needed -> Done.
Accessing Framework on ViewController:-
import UIKit
import sampleCocoaFramework
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let frameworkObject = openCocoaClass.init()
frameworkObject.samplePublicFunction()
print(frameworkObject.samplePublicVariable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Related

Swift public func not being exported from framework

I created a Swift Framework project in Xcode and I'm trying to export a function for Unity to call. I defined the function like this.
#_cdecl("showPaymentForm")
public func showPaymentSheet()-> PKPaymentAuthorizationViewController?{
NSLog("In showPaymentForm - Navtive code.")
return ApplePayLib.showPaymentSheet(label: "something", total: 9.99, mainViewController: nil)
I ran
nm -gU
against the binary and it doesn't show any functions exported called showPaymentForm. I check the "Symbols Hidden by Default" to make sure it was set to "No".
If I do the same as above in a package instead of a framework, it works fine. I didn't stick with the package project because I need to include other packages that didn't play nice with it.
Not really sure what else to check.
It turned out the issue was due to the swift file not having a target selected. Once I selected the correct target, the function as exported correctly.

Compatibility issue with Xcode generated swiftinterface file

I'm working with a Cocoa Touch framework, built for distribution, which uses Core Data managed object subclasses. When I implement the framework in a client project built with the same Xcode/Swift version as the framework, the app compiles successfully.
However, when I build the framework with Xcode 11.1 (Swift 5.1), and implement the framework in a client project using Xcode 11.2.1 (Swift 5.1.2), I get a compile error in the property declaration of the generated "swiftinterface" file. "#NSManaged not allowed on computed properties"
From Framework project NSManagedObject subclass:
extension TestEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<TestEntity> {
return NSFetchRequest<TestEntity>(entityName: "TestEntity")
}
#NSManaged public var id: Int64
}
From Framework .swiftinterface file:
#objc #NSManaged dynamic public var id: Swift.Int64 {
#objc get
#objc set(value)
}
If I remove the setter-getter, the client app builds and runs, but I see this warning:
Cannot load Swift type information; AST validation error in "my_test_framework": The module file format is too old to be used by this version of the debugger.
Thoughts, rants, lessons, and/or workarounds are welcome!

Is it possible to include typealias entries only if the code is compiled for *lower* targets?

We have a codebase that we're targeting iOS 9 through 12 and we're using Xcode 9 and 10 as a result. We always try to write our code to target the latest APIs, then add backwards compatibility if needed.
For instance, this code is in iOS 11 and newer...
UITableView.RowAnimation
However, prior to iOS 11, it's this...
UITableViewRowAnimation
To allow our code to be compiled in Xcode9 against iOS9, we've created a 'compatibility.swift' file that includes things like this...
extension UITableView {
public typealias RowAnimation = UITableViewRowAnimation
public static var automaticDimension = UITableViewAutomaticDimension
public func performBatchUpdates(_ batchFunction:() -> Void, completion:(() -> Void)? = nil){
beginUpdates()
batchFunction()
endUpdates()
completion?()
}
}
What we're trying to do is only include the above if iOS is less than 11.
I know about attributes on methods, as well as the if-checks against versioning, but from what I can tell, they don't actually exclude it from being compiled, meaning if you included the above, even if attributed, you will get compilation errors in iOS 11.
Now while I could create a second target, then include and exclude the files as needed, other languages actually let you specify compiler directive to exclude them right from within the file.
Does Swift have any such functionality? If not, how can the above be achieved in a project that's being opened in both iOS 9/Xcode 9 and iOS 11/Xcode 10?

Framework entry point in Swift

I just started playing with frameworks in xcode trying to create my own module. When making an iOS app its relatively straightforward where the entry point is ( AppDelegate )
However the framework I've created has nothing like a "main.swift" or similar method.
Do i have to create/specify the entry point on my own?
Thanks
EDIT: The reason i'd like the framework to be runnable is so that i can actually print some output while I'm making it to make sure everything works properly.
(My TL;DR is at the bottom.)
As already stated, there is no entry point like you are thinking. Instead, you should do this:
In your Framework target (I'll assume the framework is named MyFramework):
Add files, classes, properties, subclassed controls, etc. and mark things as public, private, internal, and fileprivate. (See the access level section in the Apple documentation.)
For instance:
public class MyClass1 {
public var property1 = ""
private var property2 = ""
public func myFunc() -> String {
print("Hello World!")
}
}
private class MyClass2 {
var property1 = ""
var property2 = ""
func myFunc() -> String {
return "Hello World!"
}
}
In your app target (again, assuming your framework is named myFramework):
include MyFramework
class ViewController: UIViewController {
func tryThis() {
let myClass1 = myClass1()
print(myClass1.myFunc()) // prints "Hello World!"
// the line below will generate a build error
// as myClass2 is marks private
let myClass2 = myClass2()
}
}
TL;DR
Learn your Access Levels, add code into your Framework target, and import the framework into your app.
A framework doesn't have a traditional entry point like this - it won't ever be run by itself, so it doesn't need one.
To use your framework you would create an app which linked with your framework - the entry point for the app would then call methods from inside your framework.
An answer I got from #deanWombourne points out that if anyone wants to only use the framework project for development as is and develop the framework without integrating it in an app for execution, they can just use the tests provided by the framework for an entry point.
For someone that might be new, all you need to is include unit tests to your project, press on the play button which you normally press on for running and select the wrench icon that writes "test" next to it to run the tests.

How do I import a Swift file from another Swift file?

I simply want to include my Swift class from another file, like its test
PrimeNumberModel.swift
import Foundation
class PrimeNumberModel { }
PrimeNumberModelTests.swift
import XCTest
import PrimeNumberModel // gives me "No such module 'PrimeNumberModel'"
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel() // "Use of unresolved identifier 'PrimeNumberModel'"
}
Both swift files are in the same directory.
I had the same problem, also in my XCTestCase files, but not in the regular project files.
To get rid of the:
Use of unresolved identifier 'PrimeNumberModel'
I needed to import the base module in the test file. In my case, my target is called 'myproject' and I added import myproject and the class was recognised.
UPDATE Swift 2.x, 3.x, 4.x and 5.x
Now you don't need to add the public to the methods to test then.
On newer versions of Swift it's only necessary to add the #testable keyword.
PrimeNumberModelTests.swift
import XCTest
#testable import MyProject
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel()
}
And your internal methods can keep Internal
PrimeNumberModel.swift
import Foundation
class PrimeNumberModel {
init() {
}
}
Note that private (and fileprivate) symbols are not available even with using #testable.
Swift 1.x
There are two relevant concepts from Swift here (As Xcode 6 beta 6).
You don't need to import Swift classes, but you need to import external modules (targets)
The Default Access Control level in Swift is Internal access
Considering that tests are on another target on PrimeNumberModelTests.swift you need to import the target that contains the class that you want to test, if your target is called MyProject will need to add import MyProject to the PrimeNumberModelTests:
PrimeNumberModelTests.swift
import XCTest
import MyProject
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel()
}
But this is not enough to test your class PrimeNumberModel, since the default Access Control level is Internal Access, your class won't be visible to the test bundle, so you need to make it Public Access and all the methods that you want to test:
PrimeNumberModel.swift
import Foundation
public class PrimeNumberModel {
public init() {
}
}
In the Documentation it says there are no import statements in Swift.
Simply use:
let primNumber = PrimeNumberModel()
Check target-membership of PrimeNumberModel.swift in your testing target.
In Objective-C, if you wanted to use a class in another file you had to import it:
#import "SomeClass.h"
However, in Swift, you don't have to import at all. Simply use it as if it was already imported.
Example
// This is a file named SomeClass.swift
class SomeClass : NSObject {
}
// This is a different file, named OtherClass.swift
class OtherClass : NSObject {
let object = SomeClass()
}
As you can see, no import was needed. Hope this helps.
According To Apple you don't need an import for swift files in the Same Target. I finally got it working by adding my swift file to both my regular target and test target. Then I used the bridging header for test to make sure my ObjC files that I referenced in my regular bridging header were available. Ran like a charm now.
import XCTest
//Optionally you can import the whole Objc Module by doing #import ModuleName
class HHASettings_Tests: XCTestCase {
override func setUp() {
let x : SettingsTableViewController = SettingsTableViewController()
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}
SO make sure PrimeNumberModel has a target of your test Target. Or High6 solution of importing your whole module will work
I was able to solve this problem by cleaning my build.
Top menu -> Product -> Clean
Or keyboard shortcut: Shift+Cmd+K
As of Swift 2.0, best practice is:
Add the line #testable import MyApp to the top of your tests file, where "MyApp" is the Product Module Name of your app target (viewable in your app target's build settings). That's it.
(Note that the product module name will be the same as your app target's name unless your app target's name contains spaces, which will be replaced with underscores. For example, if my app target was called "Fun Game" I'd write #testable import Fun_Game at the top of my tests.)
Check your PrimeNumberModelTests Target Settings.
If you can't see PrimeNumberModel.swift file in Build Phases/Compile Sources, add it.
You need to add a routine for the compiler to reference as an entry point, so add a main.swift file, which in this case simply creates an instance of your test file:
main.swift
PrimeNumberModelTests()
Then compile on the command line (I am using El Capitan and Swift 2.2):
xcrun -sdk macosx swiftc -emit-executable -o PrimeNumberMain PrimeNumberModel.swift PrimeNumberModelTests.swift main.swift
In this case, you will get a warning: result of initializer is unused, but the program compiles and is executable:
./PrimeNumberMain
CAVEAT: I removed the import XCTest and XCTestCase type for simplicity.
So, you need to
Import external modules you want to use
And make sure you have the right access modifiers on the class and methods you want to use.
In my case I had a swift file I wanted to unit test, and the unit test file was also a swift class. I made sure the access modifiers were correct, but the statement
import stMobile
(let's say that stMobile is our target name)
still did not work (I was still getting the 'No such module' error), I checked my target, and its name was indeed stMobile. So, I went to Build Settings, under packaging, and found the Product Module Name, and for some reason this was called St_Mobile, so I changed my import statement
import St_Mobile
(which is the Product Module Name), and everything worked.
So, to sum up:
Check your Product Module Name and use the import statement below in you unit test class
import myProductModuleName
Make sure your access modifiers are correct (class level and your methods).
Instead of requiring explicit imports, the Swift compiler implicitly searches for .swiftmodule files of dependency Swift libraries.
Xcode can build swift modules for you, or refer to the railsware blog for command line instructions for swiftc.
As #high6 and #erik-p-hansen pointed out in the answer given by #high6, this can be overcome by importing the target for the module where the PrimeNumberModel class is, which is probably the same name as your project in a simple project.
While looking at this, I came across the article Write your first Unit Test in Swift on swiftcast.tv by Clayton McIlrath. It discusses access modifiers, shows an example of the same problem you are having (but for a ViewController rather than a model file) and shows how to both import the target and solve the access modifier problem by including the destination file in the target, meaning you don't have to make the class you are trying to test public unless you actually want to do so.