How to unit test iOS package with `swift test`? - ios

I have an iOS library with Swift package manager. I am trying to run unit tests with swift test
The reason why I am not using xcodebuild is, I am trying to remove the .xcodeproj from my source control. Also, there is a warning when we create xcodeproj with spm, that generate-xcodeproj will be deprecated soon.
TL;DR
This library depends on Lottie
I tried just running swift test in the root directory, but it gives lot of errors. like /.build/checkouts/lottie-ios/lottie-swift/src/Public/Animation/AnimationView.swift:859:11: error: cannot find 'superview' in scope
I tried
swift test -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios14.5-simulator"
Then it gives the error error: module 'XCTest' was created for incompatible target x86_64-apple-macos10.15. I did not specify macos10.15 anywhere, I am on macOS 11.5.1
I can build the package using the above command by replacing test with build. What is wrong with my test command?
As I understand this is because I am not specifying the target simulator, is it possible to specify the target simulator with swift test?

swift test only works for macOS testing. It does not support cross builds for iOS.
You do not need an existing Xcode project to use xcodebuild. Run the command, xcodebuild -list to initialize a Package.swift file for testing.
See the example here.

Related

Module 'Swift' was created for incompatible target x86_64-apple-ios13.0

I'm creating an iOS framework and I want to copy some Xcode templates from my framework directory (that are not included in my .xcproject, but are in the folder that contains the project), when my framework is installed trought Cocoapods. In other words, when a developer installs my framework with Cocoapods or manually, I want to copy the templates into his Xcode Template Files folder.
I'm trying to execute a swift script file from the build phase of Xcode like this:
swift "${SRCROOT}/Folder/Folder/installer.swift"
But I get this error when I try to build it:
/<unknown>:1:1: module 'Swift' was created for incompatible target x86_64-apple-ios13.0: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator/prebuilt-modules/Swift.swiftmodule/x86_64.swiftmodule
If I execute swift installer.swift from the terminal, the script works. So, I think there is a problem with my Xcode.
My installer.swift file copies the template files to the Xcode Template Files folder.
I don't know if this way is the correct one, but I didn't find any other solution so far.
When I tried something similar I had to tell swift to compile for macOS by adding the following "shebang" comment as the first line of the swift script:
#!/usr/bin/env xcrun --sdk macosx swift

Swift generate xcodeproj for swift package with Realm dependency cpp parse error

I made a swift package with Realm (3.18.0) as dependency. I can build, test and include my package in others projects without any problems.
Recently I tried to automate my test in order to run it into a script. I've found this steps:
swift package generate-xcodeproj
xcodebuild test -destination 'name=iPhone 11' -scheme 'MyLibrary-Package'
I can generate my project but after that I cannot build it, I got errors from RealmCore on file terminate.cpp such as
ParseIssue: Expected ";" after expression
Lexical or Preprocessor Issue: Invalid suffix .5 on floating constant (Expanded from macro 'REALM_VER_CHUNK')
On my package settings, iOS supported version is 11. Swift language version is unspecified.
Any idea about why I get this error ?
Thanks you very much 🙏

Linking third party frameworks to my own framework, distributed using Carthage

We have several different proprietary API provided to us as Frameworks that offer similar functionality. The API is of course not identical, so we want to build a wrapper facade Framework around these, so our apps do not need to care which of the API they are using.
As a start, I have created a framework that uses one of these APIs and has unit tests that verifies that it works as expected. We have added the third party framework binary as a part of the project, and the Framework search path is like this:
$(inherited)
$(PROJECT_DIR)/Carthage/Build/iOS
$(PROJECT_DIR)
All non-recursive. The Cartage path is here because we use Quick and Nimble for our unit tests. I have the framework in question stored at the project root.
We want to distribute our framework using Carthage, and later also as a Cocoapod.
The problem is that when I try to build our framework by the command: carthage build --no-skip-current --platform iOS, I get the error:
*** Building scheme "Nimble-iOS" in Nimble.xcodeproj
*** Building scheme "Quick-iOS" in Quick.xcworkspace
*** Building scheme "XXX" in XXX.xcodeproj
Build Failed
Task failed with exit code 65:
/usr/bin/xcrun xcodebuild -project /Users/andre/Development/yyy/xxx/XXX.xcodeproj -scheme XXX -configuration Release -sdk iphonesimulator -destination platform=iOS\ Simulator,id=AE9E3D9D-BE2C-4AE1-AB37-C6BE8948067B -destination-timeout 3 ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES build (launched in /Users/andre/Development/yyy/XXX)
And the log contains this:
iphonesimulator/XXX.build/Objects-normal/i386/XXXLockService.o
/Users/andre/Development/yyy/XXX/XXX/LockServiceFactory.swift:7:71: error: 'PinServiceAccessImpl' is unavailable: cannot find Swift declaration for this class
case .xxx: return XXXLockService(lockService: PinServiceAccessImpl.getInstance())
^~~~~~~~~~~~~~~~~~~~
PinServiceAccess.PinServiceAccessImpl:2:12: note: 'PinServiceAccessImpl' has been explicitly marked unavailable here
open class PinServiceAccessImpl : NSObject {
When using a framework from an app, the documentation states that we should add the framework's as an embedded binary, but this option is not available when the project is a framework.
What puzzles me is that the unit tests seems to find the PinServiceAccessImpl.getInstance()!
I guess there are some details about linking that I don't know about that makes the build fails. Does anyone have an idea of what we are missing out?
The problem got solved when updating the deployment target to 11.0.

Ignore .metal files when building for iOS Simulator target

Whenever I build a project that includes a metal shader to an x86_64 target (iOS simulator), I get a dependency analysis warning:
warning: no rule to process file '[File Path]/Shaders.metal' of type sourcecode.metal for architecture x86_64
I know this isn't a huge issue but I like to keep my projects free from warnings when I build, so that when a real issue does arise, I actually notice the yellow warning triangle.
Any quick way to get Xcode to ignore metal files for simulator targets?
You can resolve this by precompiling your .metal file into a Metal library during the build step, and removing the .metal source code from your app target.
Remove .metal file from target
Select your .metal file in the project navigator, and uncheck the target that is giving you the warning.
Metal library compile script
Create a bash script called CompileMetalLib.sh in your project, alongside your .metal file, with contents like this:
xcrun -sdk iphoneos metal -c MyShader.metal -o MyShader.air
xcrun -sdk iphoneos metallib MyShader.air -o MyShader.metallib
rm MyShader.air
Make sure to give it executable permissions by running chmod +x CompileMetalLib.sh.
MyShader.air is the intermediate compile step, and MyShader.metallib is the fully compiled metal library. Read all about compiling a Metal file here
If you're compiling for OS X, change iphoneos to macosx.
Run compile script during build
Now you'll want to trigger this script in your Build Phases.
Add a New Run Script Phase to your target. The contents should look like this:
cd ${SRCROOT}/path/to/folder/containing/yourshader
./CompileMetalLib.sh
It's important to drag this step so that it happens before the Copy Bundle Resources step.
Change your code to use your compiled Metal library
You'll now load the compiled Metal library from your app bundle.
Here's some pseudo-Swift:
let metalLibPath = Bundle.main.path(forResource: "MyShader", ofType: "metallib")
let myLibrary = try metalDevice.makeLibrary(filepath: metalLibPath)
Result
You are manually compiling your .metal file with an external script, and copying the compiled library into your bundle resources. Your code loads this library. Now that you don't have any .metal files in your target, the simulator will no longer throw a warning about not being able to compile for x86_64.
As of Xcode 11, Simulator supports Metal when running on macOS Catalina. Metal files are supported during the build, including when running on macOS Mojave or when building with an older deployment target. Metal won't be functional in those scenarios but you no longer need to exclude files from the build. (Of course when running iOS 13 / tvOS 13 simulators on 10.15 Metal actually works).

How do you determine the build options that XCode uses so you can use them with xcodebuild?

I want to build my iOS projects with xcodebuild, but to do so I need to figure out all the flags and options that XCode is using to build my project when I build and run in XCode.
How can I find out what compilation flags and such XCode is using during the build process, so that I can figure out the equivalent xcodebuild command? I'm especially interested in getting this to work when I'm including external libraries like RestKit in my project.
All you need to do is tell xcodebuild the target and the configuration; everything else is contained in the project relating to the configuration.
See the manpage.

Resources