Xamarin.iOS is not seeing reference to iOS binding library - ios

I have created new Cocoa Touch Static Library in XCode.
I have written code in: StaticLibrary.m:
#import "StaticLibrary.h"
#implementation StaticLibrary
- (int)addX:(int)x toY:(int)y
{
int sum = x + y;
return sum;
}
#end
I have build project in Release-iphoneos and Release-iphonesimulator, then use terminal:
lipo -create Release-iphoneos/StaticLibrary.a Release-iphonesimulator/StaticLibrary.a -output StaticLibraryFat.a
Now I have fat library "StaticLibraryFat.a". Then I create new iOS Binding Library (Xamarin), click PPM -> Add Existing item -> StaticLibraryFat.a. So the file was added and the new libStaticLibraryFinal.linkwith.cs was created. Code inside:
using System;
using ObjCRuntime;
[assembly: LinkWith ("libStaticLibraryFinal.a", LinkTarget.Simulator, ForceLoad = true)]
I go to Mac, open terminal and use Objective Sharpie:
sharpie bind --output=StaticLibrary --namespace=StaticLibrary ~/Desktop/StaticLibrary/StaticLibrary/*.h --sdk=iphoneos12.1 -scope ~/Desktop/StaticLibrary
Now I copy content of ApiDefinitions.cs into iOS Binding Library (Xamarin) - to ApiDefinitions.cs in project.
ApiDefinition.cs
namespace NativeLibrary
{
[BaseType(typeof(NSObject))]
interface StaticLibrary
{
[Export("addX:toY:")]
int AddX(int x, int y);
}
}
I build iOS Binding Library (Xamarin). In folder bin -> Debug there is NativeLibrary.dll.
I create new iOS App (Xamarin). PPM -> Add Reference -> Project -> Solution -> iOS Binding Library (Xamarin).
In ViewController.cs I write:
using NativeLibrary
and
NativeLibrary.AddX(1, 2);
but there is an error
"Using directive is unnecessary. The type or namespace name "Native
Library" could not be found (are you missing a using directive or an
assembly reference?)
What am I doing wrong?
When I add reference to iOS Class library then the reference is working perfectly. Why reference to iOS Binding Library is not working?

Ok, I have solved it. There was a problem with different namespaces, so Visual Studio can not connect everything. Namespace at ApiDefinition.cs and Structs.cs must be the same as name of iOSBindingLibrary. The generated .dll file has name "NativeLibrary.dll" and I change it to namespace.dll, then at iOS application I add reference to this dll. then using directive (using "namespace"). In class I write name of XCode's library and create new object. Everything is working perfectly.

This is Crazy, but in my case,
just create the ios binding project in a different solution and add the binding project DLL directly in the ios project. it will work fine.

Related

Adding native swift code to NativeScript application

I'm trying to add native swift code to my NativeScript app. According to these instructions in the documentation I can just add a swift source file to App_Resources/iOS/src/ and then use any publicly exposed classes directly in my TypeScript code.
Unfortunately this just doesn't work. I'll just get Cannot find name 'TestClass' and that's it.
Steps to reproduce:
Get a fresh NS project with ios tns create my-app-name --template tns-template-blank-ts
Update: I actually created the App with vue init nativescript-vue/vue-cli-template testapp. That seems to have caused the problems.
Add a TestClass.swift to App_Resources/iOS/src/
import Foundation
public class TestClass: NSObject {
#objc public func echo(param: String) -> String {
return param
}
}
Instantiate it in any TypeScript source file let instance = new TestClass()
Do tns debug ios
Compilation will fail with Cannot find name 'TestClass'
I have also tried generating TypeScript typings with TNS_TYPESCRIPT_DECLARATIONS_PATH="$(pwd)/typings" tns build ios or or just delcaring it as any with declare let KeyCommander: any; to eliminate the possibility that this is a TS related problem. The first approach doesn't generate any typings for my custom class so the TypeScript code will still not compile. The second approach let's the TS code compile but crashes on execution with JS ERROR ReferenceError: Can't find variable: TestClass.
I have also verified that the swift file is indeed getting compiled by inserting a syntax error which will crash the build process.
My NativeScript version is 6.4.0.
What am I missing?
Update: I just realized I actually created the App with vue init nativescript-vue/vue-cli-template testapp. I verified that as mentioned Tyler Blake's answer in an app created with the tns cli the described process actually works. In an app I just freshly created with vue init it doesn't, the objc!nsswiftsupport.d.ts is not being generated.
The question now is: What's causing the difference?
I followed your steps and I was able to get the typings to generate in objc!nsswiftsupport.d.ts. After you generate typings do you have that file with these contents?
declare class TestClass extends NSObject {
static alloc(): TestClass; // inherited from NSObject
static new(): TestClass; // inherited from NSObject
echoWithParam(param: string): string;
}
This shows that NS is able to pick up the Swift code.
All you need to do now is add tns-platform-declarations then in the references.d.ts file, add a line that points to the objc!nsswiftsupport.d.ts file. Then you'll get intellisense in your TS code.
Something like this:
/// <reference path="./typings/objc!nsswiftsupport.d.ts" />
Hope this helps!
I was able to solve the problem by inspecting the differences between the templates created with tns-cli and vue init. The difference is that the vue init template ships with an outdated version of the nativescript platform. You can just simply change
"tns-ios": {
"version": "6.0.1"
}
to version 6.4.0 (which the version the tns-cli template comes with) and then the process will work as described in the documentation.

Unity Script Compiling everytime I run the game

My Unity compiles scripts during runtime every time I run the game (Spinning compile icon indicator), even if I changed no code, it only occurs when I am using OS X and using the IOS platform.
Any ideas?
Check your class in "Editor" whether some editor class use compile cmd in runtime
This is due to a script processing the scene during runtime. It is located in Assets/Editor/UnityAds/UnityAdsBuildProcessor
I briefly commented it out.
Its content:
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.Advertisements;
public class UnityAdsBuildProcessor : Editor
{
[PostProcessScene]
public static void OnPostprocessScene ()
{
AdvertisementSettings.enabled = true;
AdvertisementSettings.initializeOnStartup = false;
}
}

Importing CommonCrypto in a Swift framework

How do you import CommonCrypto in a Swift framework for iOS?
I understand how to use CommonCrypto in a Swift app:
You add #import <CommonCrypto/CommonCrypto.h> to the bridging header.
However, Swift frameworks don't support bridging headers. The documentation says:
You can import external frameworks that have a pure Objective-C codebase, a pure Swift codebase, or a mixed-language codebase. The
process for importing an external framework is the same whether the
framework is written in a single language or contains files from both
languages. When you import an external framework, make sure the
Defines Module build setting for the framework you’re importing is set
to Yes.
You can import a framework into any Swift file within a different
target using the following syntax:
import FrameworkName
Unfortunately, import CommonCrypto doesn't work. Neither does adding #import <CommonCrypto/CommonCrypto.h> to the umbrella header.
Something a little simpler and more robust is to create an Aggregate target called "CommonCryptoModuleMap" with a Run Script phase to generate the module map automatically and with the correct Xcode/SDK path:
The Run Script phase should contain this bash:
# This if-statement means we'll only run the main script if the CommonCryptoModuleMap directory doesn't exist
# Because otherwise the rest of the script causes a full recompile for anything where CommonCrypto is a dependency
# Do a "Clean Build Folder" to remove this directory and trigger the rest of the script to run
if [ -d "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap" ]; then
echo "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap directory already exists, so skipping the rest of the script."
exit 0
fi
mkdir -p "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap"
cat <<EOF > "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap/module.modulemap"
module CommonCrypto [system] {
header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
EOF
Using shell code and ${SDKROOT} means you don't have to hard code the Xcode.app path which can vary system-to-system, especially if you use xcode-select to switch to a beta version, or are building on a CI server where multiple versions are installed in non-standard locations. You also don't need to hard code the SDK so this should work for iOS, macOS, etc. You also don't need to have anything sitting in your project's source directory.
After creating this target, make your library/framework depend on it with a Target Dependencies item:
This will ensure the module map is generated before your framework is built.
macOS note: If you're supporting macOS as well, you'll need to add macosx to the Supported Platforms build setting on the new aggregate target you just created, otherwise it won't put the module map in the correct Debug derived data folder with the rest of the framework products.
Next, add the module map's parent directory, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap, to the "Import Paths" build setting under the Swift section (SWIFT_INCLUDE_PATHS):
Remember to add a $(inherited) line if you have search paths defined at the project or xcconfig level.
That's it, you should now be able to import CommonCrypto
Update for Xcode 10
Xcode 10 now ships with a CommonCrypto module map making this workaround unnecessary. If you would like to support both Xcode 9 and 10 you can do a check in the Run Script phase to see if the module map exists or not, e.g.
COMMON_CRYPTO_DIR="${SDKROOT}/usr/include/CommonCrypto"
if [ -f "${COMMON_CRYPTO_DIR}/module.modulemap" ]
then
echo "CommonCrypto already exists, skipping"
else
# generate the module map, using the original code above
fi
You can actually build a solution that "just works" (no need to copy a module.modulemap and SWIFT_INCLUDE_PATHS settings over to your project, as required by other solutions here), but it does require you to create a dummy framework/module that you'll import into your framework proper. We can also ensure it works regardless of platform (iphoneos, iphonesimulator, or macosx).
Add a new framework target to your project and name it after the system library, e.g., "CommonCrypto". (You can delete the umbrella header, CommonCrypto.h.)
Add a new Configuration Settings File and name it, e.g., "CommonCrypto.xcconfig". (Don't check any of your targets for inclusion.) Populate it with the following:
MODULEMAP_FILE[sdk=iphoneos*] = \
$(SRCROOT)/CommonCrypto/iphoneos.modulemap
MODULEMAP_FILE[sdk=iphonesimulator*] = \
$(SRCROOT)/CommonCrypto/iphonesimulator.modulemap
MODULEMAP_FILE[sdk=macosx*] = \
$(SRCROOT)/CommonCrypto/macosx.modulemap
Create the three referenced module map files, above, and populate them with the following:
iphoneos.modulemap
module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
iphonesimulator.modulemap
module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
macosx.modulemap
module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
(Replace "Xcode.app" with "Xcode-beta.app" if you're running a beta version. Replace 10.11 with your current OS SDK if not running El Capitan.)
On the Info tab of your project settings, under Configurations, set the Debug and Release configurations of CommonCrypto to CommonCrypto (referencing CommonCrypto.xcconfig).
On your framework target's Build Phases tab, add the CommonCrypto framework to Target Dependencies. Additionally add libcommonCrypto.dylib to the Link Binary With Libraries build phase.
Select CommonCrypto.framework in Products and make sure its Target Membership for your wrapper is set to Optional.
You should now be able to build, run and import CommonCrypto in your wrapper framework.
For an example, see how SQLite.swift uses a dummy sqlite3.framework.
I found a GitHub project that successfully uses CommonCrypto in a Swift framework: SHA256-Swift. Also, this article about the same problem with sqlite3 was useful.
Based on the above, the steps are:
1) Create a CommonCrypto directory inside the project directory. Within, create a module.map file. The module map will allow us to use the CommonCrypto library as a module within Swift. Its contents are:
module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/usr/include/CommonCrypto/CommonCrypto.h"
link "CommonCrypto"
export *
}
2) In Build Settings, within Swift Compiler - Search Paths, add the CommonCrypto directory to Import Paths (SWIFT_INCLUDE_PATHS).
3) Finally, import CommonCrypto inside your Swift files as any other modules. For example:
import CommonCrypto
extension String {
func hnk_MD5String() -> String {
if let data = self.dataUsingEncoding(NSUTF8StringEncoding)
{
let result = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH))
let resultBytes = UnsafeMutablePointer<CUnsignedChar>(result.mutableBytes)
CC_MD5(data.bytes, CC_LONG(data.length), resultBytes)
let resultEnumerator = UnsafeBufferPointer<CUnsignedChar>(start: resultBytes, length: result.length)
let MD5 = NSMutableString()
for c in resultEnumerator {
MD5.appendFormat("%02x", c)
}
return MD5
}
return ""
}
}
Limitations
Using the custom framework in another project fails at compile time with the error missing required module 'CommonCrypto'. This is because the CommonCrypto module does not appear to be included with the custom framework. A workaround is to repeat step 2 (setting Import Paths) in the project that uses the framework.
The module map is not platform independent (it currently points to a specific platform, the iOS 8 Simulator). I don't know how to make the header path relative to the current platform.
Updates for iOS 8 <= We should remove the line link "CommonCrypto", to get the successful compilation.
UPDATE / EDIT
I kept getting the following build error:
ld: library not found for -lCommonCrypto for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Unless I removed the line link "CommonCrypto" from the module.map file I created. Once I removed this line it built ok.
This answer discusses how to make it work inside a framework, and with Cocoapods and Carthage
🐟 modulemap approach
I use modulemap in my wrapper around CommonCrypto https://github.com/onmyway133/arcane, https://github.com/onmyway133/Reindeer
For those getting header not found, please take a look https://github.com/onmyway133/Arcane/issues/4 or run xcode-select --install
Make a folder CCommonCrypto containing module.modulemap
module CCommonCrypto {
header "/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
Go to Built Settings -> Import Paths
${SRCROOT}/Sources/CCommonCrypto
🌳 Cocoapods with modulemap approach
Here is the podspec https://github.com/onmyway133/Arcane/blob/master/Arcane.podspec
s.source_files = 'Sources/**/*.swift'
s.xcconfig = { 'SWIFT_INCLUDE_PATHS' =>
'$(PODS_ROOT)/CommonCryptoSwift/Sources/CCommonCrypto' }
s.preserve_paths = 'Sources/CCommonCrypto/module.modulemap'
Using module_map does not work, see https://github.com/CocoaPods/CocoaPods/issues/5271
Using Local Development Pod with path does not work, see https://github.com/CocoaPods/CocoaPods/issues/809
That's why you see that my Example Podfile https://github.com/onmyway133/CommonCrypto.swift/blob/master/Example/CommonCryptoSwiftDemo/Podfile points to the git repo
target 'CommonCryptoSwiftDemo' do
pod 'CommonCryptoSwift', :git => 'https://github.com/onmyway133/CommonCrypto.swift'
end
🐘 public header approach
Ji is a wrapper around libxml2, and it uses public header approach
It has a header file https://github.com/honghaoz/Ji/blob/master/Source/Ji.h with Target Membership set to Public
It has a list of header files for libxml2 https://github.com/honghaoz/Ji/tree/master/Source/Ji-libxml
It has Build Settings -> Header Search Paths
$(SDKROOT)/usr/include/libxml2
It has Build Settings -> Other Linker Flags
-lxml2
🐏 Cocoapods with public header approach
Take a look at the podspec https://github.com/honghaoz/Ji/blob/master/Ji.podspec
s.libraries = "xml2"
s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2', 'OTHER_LDFLAGS' => '-lxml2' }
🐝 Interesting related posts
How to call C from Swift?
https://spin.atomicobject.com/2015/02/23/c-libraries-swift/
Good news! Swift 4.2 (Xcode 10) finally provides CommonCrypto!
Just add import CommonCrypto in your swift file.
WARNING: iTunesConnect may reject apps that are using this method.
New member on my team accidentally broke the solution given by one of the top answers, so I decided to consolidate it in a small wrapper project called CommonCryptoModule. You can install it manually or via Cocoapods:
pod 'CommonCryptoModule', '~> 1.0.2'
Then, all you have to do is to import the module where you need CommonCrypto, like so:
import CommonCryptoModule
Hope someone else finds this useful.
I think I have an improvement to Mike Weller's excellent work.
Add a Run Script phase before the Compile Sources phase containing this bash:
# This if-statement means we'll only run the main script if the
# CommonCrypto.framework directory doesn't exist because otherwise
# the rest of the script causes a full recompile for anything
# where CommonCrypto is a dependency
# Do a "Clean Build Folder" to remove this directory and trigger
# the rest of the script to run
FRAMEWORK_DIR="${BUILT_PRODUCTS_DIR}/CommonCrypto.framework"
if [ -d "${FRAMEWORK_DIR}" ]; then
echo "${FRAMEWORK_DIR} already exists, so skipping the rest of the script."
exit 0
fi
mkdir -p "${FRAMEWORK_DIR}/Modules"
cat <<EOF > "${FRAMEWORK_DIR}/Modules/module.modulemap"
module CommonCrypto [system] {
header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h"
export *
}
EOF
ln -sf "${SDKROOT}/usr/include/CommonCrypto" "${FRAMEWORK_DIR}/Headers"
This script constructs a bare bones framework with the module.map in the correct place and then relies on Xcode's automatic search of BUILT_PRODUCTS_DIR for frameworks.
I linked the original CommonCrypto include folder as the framework's Headers folder so the result should also function for Objective C projects.
For anyone using swift 4.2 with Xcode 10:
CommonCrypto module is now provided by the system, so you can directly import it like any other system framework.
import CommonCrypto
#mogstad has been kind enough to wrap #stephencelis solution in a Cocoapod:
pod 'libCommonCrypto'
The other pods available did not work for me.
The modulemap solutions can be good, and are robust against SDK changes, but I've found them awkward to use in practice, and not as reliable as I'd like when handing things out to others. To try to make it all more foolproof, I went a different way:
Just copy the headers.
I know, fragile. But Apple almost never makes significant changes to CommonCrypto and I'm living the dream that they will not change it in any significant way without also finally making CommonCrypto a modular header.
By "copy the headers" I mean "cut and paste all of the headers you need into one massive header in your project just like the preprocessor would do." As an example of this that you can copy or adapt, see RNCryptor.h.
Note that all of these files are licensed under APSL 2.0, and this approach intentionally maintains the copyright and license notices. My concatenation step is licensed under MIT, and that only applies up to the next license notice).
I am not saying this is a beautiful solution, but so far it seems to have been an incredibly simple solution to both implement and support.
I know this is an old question. But I figure out an alternative way to use the library in Swift project, which might be helpful for those who don't want to import framework introduced in these answers.
In Swift project, create a Objective-C bridging header, create NSData category (or custom class that to use the library) in Objective-C. The only drawback would be that you have to write all implementation code in Objective-C.
For example:
#import "NSData+NSDataEncryptionExtension.h"
#import <CommonCrypto/CommonCryptor.h>
#implementation NSData (NSDataEncryptionExtension)
- (NSData *)AES256EncryptWithKey:(NSString *)key {
//do something
}
- (NSData *)AES256DecryptWithKey:(NSString *)key {
//do something
}
And then in your objective-c bridging header, add this
#import "NSData+NSDataEncryptionExtension.h"
And then in Swift class do similar thing:
public extension String {
func encryp(withKey key:String) -> String? {
if let data = self.data(using: .utf8), let encrypedData = NSData(data: data).aes256Encrypt(withKey: key) {
return encrypedData.base64EncodedString()
}
return nil
}
func decryp(withKey key:String) -> String? {
if let data = NSData(base64Encoded: self, options: []), let decrypedData = data.aes256Decrypt(withKey: key) {
return decrypedData.UTF8String
}
return nil
}
}
It works as expected.
I've added some cocoapods magic to jjrscott's answer in case you need to use CommonCrypto in your cocoapods library.
1) Add this line to your podspec:
s.script_phase = { :name => 'CommonCrypto', :script => 'sh $PROJECT_DIR/../../install_common_crypto.sh', :execution_position => :before_compile }
2) Save this in your library folder or wherever you like (however don't forget to change the script_phase accordingly ...)
# This if-statement means we'll only run the main script if the
# CommonCrypto.framework directory doesn't exist because otherwise
# the rest of the script causes a full recompile for anything
# where CommonCrypto is a dependency
# Do a "Clean Build Folder" to remove this directory and trigger
# the rest of the script to run
FRAMEWORK_DIR="${BUILT_PRODUCTS_DIR}/CommonCrypto.framework"
if [ -d "${FRAMEWORK_DIR}" ]; then
echo "${FRAMEWORK_DIR} already exists, so skipping the rest of the script."
exit 0
fi
mkdir -p "${FRAMEWORK_DIR}/Modules"
echo "module CommonCrypto [system] {
header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h"
export *
}" >> "${FRAMEWORK_DIR}/Modules/module.modulemap"
ln -sf "${SDKROOT}/usr/include/CommonCrypto" "${FRAMEWORK_DIR}/Headers"
Works like a charm :)
I'm not sure if something's changed with Xcode 9.2 but it's now much simpler to achieve this. The only things I had to do are create a folder called "CommonCrypto" in my framework project directory and create two files inside it, one called "cc.h" as follows:
#include <CommonCrypto/CommonCrypto.h>
#include <CommonCrypto/CommonRandom.h>
And another called module.modulemap:
module CommonCrypto {
export *
header "cc.h"
}
(I don't know why you can't reference header files from the SDKROOT area directly in a modulemap file but I couldn't get it to work)
The third thing is to find the "Import Paths" setting and set to $(SRCROOT).
In fact you can set it to whatever folder you want the CommonCrypto folder to be under, if you don't want it at the root level.
After this you should be able to use
import CommonCrypto
In any swift file and all the types/functions/etc. are available.
A word of warning though - if your app uses libCommonCrypto (or libcoreCrypto) it's exceptionally easy for a not-too-sophisticated hacker to attach a debugger to your app and find out what keys are being passed to these functions.
In case you have the below issue :
ld: library not found for -lapple_crypto
clang: error: linker command failed with exit code 1 (use -v to see invocation)
In Xcode 10, Swift 4.0. CommonCrypto is a part of the framework.
Add
import CommonCrypto
Remove
CommonCrpto lib file from link binary with libraries from Build
phases
import CommonCrypto from Bridging header
This worked for me!
It happened the same to me after updating Xcode.
I tried everything I can do such as reinstalling cocoapods and cleaning the project, but it didn't work.
Now it's been solved after restart the system.
It's very simple. Add
#import <CommonCrypto/CommonCrypto.h>
to a .h file (the bridging header file of your project). As a convention you can call it YourProjectName-Bridging-Header.h.
Then go to your project Build Settings and look for Swift Compiler - Code Generation. Under it, add the name of your bridging header to the entry "Objetive-C Bridging Header".
You're done. No imports required in your Swift code. Any public Objective-C headers listed in this bridging header file will be visible to Swift.

How to build Unity3d Plugin for iOS

I have a very tiny Objective-C library built for iOS and I want to export it to Unity. I understand the basic process of writing a csharp wrapper that marshals all the invocations to native library, but I completely have no idea where to start. Could anyone please explain step-by-step how to create a unity package with my library so I could also distribute it to other developers.
Unity3d documentation is pretty brief and does not explain anything.
Thanks.
Okay, after playing few days with Unity3d on Mac I finally figured it out. All the code in this guide is dummy. I have written this stuff in 15 minutes or so, so don't be bothered by mistakes and typos.
1) Open Unity, create new project (File -> New Project) and save it somewhere
2) When the project is generated it has the following structure:
ProjectName/Assets (That's what you need)
ProjectName/Library (Nevermind what's there)
ProjectName/ProjectSettings (You don't care about it)
ProjectName/ProjectName.sln (MonoDevelop project)
3) Go to ProjectName/Assets and create the following folders: Plugins/iOS, so in the end you'll have a folder structure like this: ProjectName/Assets/Plugins/iOS
4) Put your compiled library (.a) file and necessary headers inside of ProjectName/Assets/Plugins/iOS or copy the source code of your library there (.mm, .h, .m, etc..). Remember, normally you can only access C-functions from C#, so you'll have to wrap your Objective-C stuff in C-code somehow, in my case all Objective-C objects were implemented in a form of Singleton so it wasn't hard to make a C-style wrapper around, for instance:
CWrapper.h:
extern "C" void MySDKFooBarCFunction();
CWrapper.mm
#import "CWrapper.h"
#import "MyObjectiveCLibrary.h" // your actual iOS library header
void MySDKFooBarCFunction() {
[MyObjectiveCLibrary doSomeStuff];
}
5) Then go to ProjectName/Assets and create a folder for CSharp wrapper class(es), call it whatever you want, for example: ProjectName/Assets/MySDK
6) Inside of MySDK folder create MySDK.cs file, the dummy example of C# wrapper would look like this:
using UnityEngine;
using System;
using System.Runtime.InteropServices;
public class MySDK
{
// import a single C-function from our plugin
[DllImport ("__Internal")]
private static extern void MySDKFooBarCFunction();
// wrap imported C-function to C# method
public static void FooBarCFunction() {
// it won't work in Editor, so don't run it there
if(Application.platform != RuntimePlatform.OSXEditor) {
MySDKFooBarCFunction();
}
}
}
7) Create a shell script to pack this stuff into .unitypackage and put it next to your project folder (not inside). Adjust EXPORT_PATH and PROJECT_PATH variables in the script for your needs.
#!/bin/sh
WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
UNITY_BIN="/Applications/Unity/Unity.app/Contents/MacOS/Unity"
EXPORT_PATH="${WORKDIR}/ProjectName.unitypackage"
PROJECT_PATH="${WORKDIR}/ProjectName"
ASSETS_PATH="Assets"
$UNITY_BIN -batchmode -quit \
-logFile export.log \
-projectPath $PROJECT_PATH \
-exportPackage $ASSETS_PATH $EXPORT_PATH
8) Run the created bash script to get your package build. All stuff from Assets will be included in XCode project for your Unity Project when you generate it via File -> Build Settings in Unity Editor. You can use generated package to distribute your code to other developers so they can simply include your library to their Unity projects by double clicking on the package file.
Don't forget to shutdown Unity Editor when you run this script, otherwise it may fail to build a package.
If you have some issues and package does not show up, this script always prints log to export.log
Next steps make sense only if you want to make a Demo unity project for your library (good for testing at least)
9) You can put created Unity project (ProjectName.unity) to Assets/MySDKDemo so you have a demo inside of your package.
10) Create a simple script for your Demo Unity3d scene at Assets/MySDKDemo/MySDKDemo.cs, for example:
using UnityEngine;
using System;
using System.Collections;
public class MySDKDemo : MonoBehaviour
{
private GUIStyle labelStyle = new GUIStyle();
private float centerX = Screen.width / 2;
// Use this for initialization
void Start ()
{
labelStyle.fontSize = 24;
labelStyle.normal.textColor = Color.black;
labelStyle.alignment = TextAnchor.MiddleCenter;
}
void OnGUI ()
{
GUI.Label(new Rect(centerX - 200, 20, 400, 35), "MySDK Demo", labelStyle);
if (GUI.Button(new Rect(centerX - 75, 80, 150, 35), "DoStuff"))
{
MySDK.FooBarCFunction();
}
}
}
11) Go to Unity Editor. Find the "Main Camera" in left sidebar in Unity Editor, select it and in the bottom of Inspector panel (right sidebar) click on AddComponent, select Scripts -> MySDKDemo script
12) Build the XCode project and run on device.
Few notes
1) Plugins don't work in Unity Editor, simply because they're not compiled in the real-time, well, not sure but probably until you use C# in your plugins, probably C# stuff gets linked immidiately and works in Editor environment.
2) This post does not cover marshaling, or data/memory management between native <-> managed code, as it is very well documented.
Interop with Native Libraries # Mono project
3) Callbacks from C# to C can be passed using C# delegates, on C-side you use standard functions declarations, on C# side you declare delegates with the same signature. It seems that booleans, integers and strings (C: char*) are marshalled flawlessly (I don't talk about memory management policy and who's responsible to release memory or return value policies).
However it will not work on iOS builds out-of-box due to platform limitations, but C#-to-C callbacks still can be implemented using MonoPInvokeCallbackAttribute, useful links on this topic:
Reverse Callbacks # Xamarin Docs
MonoPInvokeCallbackAttribute example # Xamarin Forums
Actually in Unity 4 there's AOT.MonoPInvokeCallbackAttribute already implemented, it's limited to static delegates that can be passed to unmanaged code, but still better than nothing.
4) There's a way to get Unity RootViewController using UnityGetGLViewController function. Just declare this function in your implementation file, i.e.:
extern UIViewController *UnityGetGLViewController();
And use UnityGetGLViewController() whenever you need to get an access to RootViewController.
5) There's much more magic and ugly stuff in details, keep your C interfaces as simple as possible otherwise marshalling can become your nightmare and also keep in mind that managed-to-unmanaged is generally expensive.
6) You definitely use some frameworks in your native code and you don't want linker problems. For example, if you use Keychain in your library then you need to include Security.framework into Xcode project.
I suggest to give a try to XUPorter, it helps Unity to integrate any additional dependencies into Xcode project.
Good luck!

iOS zxing: no member named BarcodeFormat_AZTEC

I'm having trouble getting the latest version of the ZXing library up and running. I've downloaded the latest source from http://zxing.googlecode.com/svn/trunk/iphone/ and the ZXing 2.0 release from http://code.google.com/p/zxing/downloads/list
I've setup the folder structure like so:
project/iphone/ZXingWidget/ZXingWidget.xcodeproj
project/cpp/ios.xcodeproj
When I open up ZXingWidget.xcodeproj and build, I get an error in CBarcodeFormat.mm that there is "no member named 'BarcodeFormat_AZTEC' in namespace 'zxing'". However, if I look in CBarcodeFormat.h, there is definitely an item in the enum called BarcodeFormat_AZTEC.
I've also noticed there are some files missing from the project (they're red in the project navigator). Specifically, these files:
- CoreSrc/zxing/common/reedsolomon/GenericGF.cpp
- CoreSrc/zxing/common/reedsolomon/GenericGF.h
- CoreSrc/zxing/common/reedsolomon/GenericGFPoly.cpp
- CoreSrc/zxing/common/reedsolomon/GenericGF.h
- CoreSrc/zxing/common/aztec/*
Any ideas what I might be doing wrong?

Resources