singleton in framework with error: initializer is inaccessible due to 'private' protection level - ios

So, I made a framework in swift and at first I wanted to use a singleton class. I built it and put the .Framework file into a new project to test it. Than I got this error:
'getInstance' is inaccessible due to 'internal' protection level
. I tried looking for anyone with the same problem, but nothing I found worked. It might be because its a framework. After hours of meaningless searching, I gave up on the singleton and I got almost the same error with a normal class.
'mySDK' initializer is inaccessible due to 'private' protection level
I tried making the class public, the initializer public, but nothing seems to change. Anyone experienced any problem like this? I never worked on frameworks before, so maybe its the obj-c header that have to be modified. If you need any more information, please just ask.
Thank you all, in advance.
Edit:
This is the getInstance func. I wrote it only, because the mySDK.myInstance seemed to give the same error.
static let myInstance = mySDK()
public static func getInstance() -> mySDK {
return myInstance
}

I don't know what caused the error, but I managed to fix it by creating a new project, than copy pasting the code from the old to the new.
I found the source of the problem. If I turned off the build active architecture only option in the build settings of the framework, It gave me this error.

You can create a singleton class in a framework like this. Please make the public Class and public static getInstance function.
public class ClassName {
// Singleton instance
private static var instance : ClassName?
/********************* Singleton Instance ************************************/
public static func getInstance() -> ClassName {
if (instance != nil) {
return instance!
}
// Initialize instance.
instance = ClassName()
return instance!
}
}

Related

Swift: import framework but can't find class in it

I import a swift framework to a swift project, but when I call the class in this framework, Xcode throw a compile error "Use of undeclared type 'xxxx(class name)' ".
I feel Xcode have found the framework, otherwise it will complaint "can't find xxx(framework name)".
But why Xcode can't find the class of this framework.
I have tried remove and re-add framework, and delete DeivedData files, but all of them not work. I haven't use CocoaPods to import framework.
Any idea?
In a Framework that was built for Release (and not Debug), the class symbols need to be accessible.
Make sure you are you trying to access Public or Open classes from your Swift framework.
// set the Framework class to Public
public class rnHello{
// set the initializer to public, otherwise you cannot invoke class
public init() {
}
// set the function to public, as it defaults to internal
public static func world() {
print("hello from a static method")
}
}
Now you can access this via your Swift code or if you attach lldb using:
lldb) po rnHello.world()
Make sure that the FrameWorkSearch path in the BuildSettings of the project is reflecting the correct path to your framework.

The Type initializer for 'Realms.Realm' threw an exception

I am trying to implement Realm on a smaller Xamarin/Mvvmcross/iOS/Droid project in order to test it's ability to replace SQLite.
I have it working well on the iOS project but am getting exceptions on the Droid project when attempting to call Realm.GetInstance();
The Type initializer for 'Realms.Realm' threw an exception
Inner Exception
System.Reflection.ReflectionTypeLoadException
The classes in the module cannot be loaded.
I have narrowed it what I believe is an issue with reflection if the MvvmCross setup occurs before the Realm dll is loaded.
For example if I call Realm.GetInstance() in any activity that inherits from MvxActivity or MvxAppCompatActivity (or anywhere in the Mvvmcross Setup / CreateApp process) the exception occurs.
If however I call var db = Realm.GetInstance() (& db.Close()) from a normal Droid Activity first, and then start the Mvx Setup process, by starting an MvxActivity, from the Droid Activity it works fine, and continues to work through the application lifecycle.
Likewise if I subclass Application and open a Realm instance in OnCreate() and close it Real will initialise anywhere else in the application.
sample code
//works
[Application]
public class CustomApplication : Application
{
public CustomApplication (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
var db = Realm.GetInstance ();
db.Close ();
}
}
//does not work unless Realm.GetInstance() has already been called once
[Activity(Label = "View for FirstViewModel")]
public class FirstView : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.FirstView);
var db = Realm.GetInstance ();
db.Close ();
}
}
I've put a test project on github at https://github.com/vurtigo/TestRealm
This is an unfortunate mix-up between Realm and Xamarin Android.
The static constructor on the Realm class walks through all the assemblies in the current AppDomain to discover all the types that inherit from RealmObject. However, if at the time Xamarin Android builds Java binding code this will define a new System.Reflection.Emit.AssemblyBuilder assembly that will raise a TypeLoadException when its types are enumerated (see Bug 39679 - ReflectionTypeLoadException after some reflection stuff).
The workaround is to cause the Realm static constructor to be invoked before any of the MvvmCross code causes Xamarin Android to emit binding code. You can do that by accessing any of the static members on Realm such as ReferenceEquals or even by including it in a typeof(Realm) expression. I suppose MvxApplication.Initialize() is a good place to do it.
In any case, I have proposed a fix that will ignore AssemblyBuilder instances in general. The very next Realm release should include it and you'll be able to delete the workaround code as soon as you upgrade.

How to access an internal Swift class in Objective-C within the same framework?

Working on a mixed framework. imported inside the Obj-C file but the internal classes are not visible, only the public ones.
The documentation clearly states the internal clasees should be available between Swift and Obj-C:
Importing Swift into Objective-C To import a set of Swift files in the same framework target as your Objective-C code, you don’t
need to import anything into the umbrella header for the framework.
Instead, import the Xcode-generated header file for your Swift code
into any Objective-C .m file you want to use your Swift code from.
Because the generated header for a framework target is part of the
framework’s public interface, only declarations marked with the public
modifier appear in the generated header for a framework target. You
can still use Swift methods and properties that are marked with the
internal modifier from within the Objective-C part of your framework,
as long they are declared within a class that inherits from an
Objective-C class. For more information on access-level modifiers, see
Access Control in The Swift Programming Language (Swift 2).
Code Sample (Create a new project with a framework)
// SwiftObject.swift
public class SwiftObject: NSObject {
public class func doSomething() {}
}
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
// SomeObject.m file
#implementation SomeObject
- (void)someMethod {
[SwiftObject doSomething];
}
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Use of undeclared identifier
}
#end
As indicated in the docs, declarations marked with internal modifier don't appear in the generated header, so the compiler does not know about them and thus complaints. Of course, you could send messages using performSelector approach, but that's not convenient and bug-prone. We just need to help the compiler know that those declarations are there.
First, we need to use #objc attribute variant that allows you to specify name for your symbol in Objective-C:
// SwiftObject.swift
#objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
And then you just need to create #interface declaration with the methods you want to use in your code - so the compiler will be happy, and also apply SWIFT_CLASS macro with the symbol name you've specified earlier - so the linker would pick the actual implementation:
// SomeObject.m file
SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
#interface YetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
#implementation SomeObject
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Should work now !!!
}
#end
I've used the interface declaration in .m file just for clarity, the better option would be to combine such declarations in .h file, and include it.
By declaring methods in that interface we're making a promise to compiler, and it won't complain if you'll put there a method that does not exist (or with wrong signature, etc.) Obviously, you'll crash in runtime in that case - so be cautious.
For me it just worked by checking: "Allow app extension API only". You find it by going to the project setting, select your target and then it is in the General tab under Deployment Info.
Can someone explain to me, why this does solve the problem?
While the above solution works (https://stackoverflow.com/a/33159964/5945317), it seems overly complicated and unintuitive:
Complicated, because it seems to add more things than necessary – I will provide a smoother solution below.
Unintuitive, because the objc macro SWIFT_CLASS resolves to SWIFT_RUNTIME_NAME, and the provided value is not actually the runtime name – nor is the objc class name in the header matching the Swift attribute param in #objc. Still, surprisingly, the solution works – but to me it is not clear why.
Here is what we have tested in our own project, and believe to be the better solution (using the example above):
// YetAnotherSwiftObject.swift
#objc(OBJCPREFIXYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
#objc internal class func doSomething() {}
}
// OBJCPREFIXYetAnotherSwiftObject.h
#interface OBJCPREFIXYetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
That's it. The interface looks like a regular objc interface. This gives the added benefit that you can include it in other header files (which you cannot do if you use the SWIFT_CLASS macro, as it comes from the autogenerated Swift header file, which in turn you cannot include in an objc header, due to circular dependency).
On the Swift side, the only thing relevant is that you provide the class with the proper objc name. Mind that I only used the name prefix for language consistency – you can even just use YetAnotherSwiftObject everywhere (i.e., in the objc header and in the #objc attribute in Swift – but you need to keep this attribute with explicit naming in any case, and need to keep it consistent with the class name in the header).
This also makes your life easier if you're in the process of converting your objc framework step by step to Swift. You just keep the objc header as before, and now provide the implementation in Swift.
Methods and properties that are marked with the internal modifier and declared within a class that inherits from an Objective-C class are accessible to the Objective-C runtime.
so let's make use of that:
class MyInternalClass: NSObject {
#objc var internalProperty = 42
}
#interface MyPublicClass()
#end
#implementation MyPublicClass
+ (void) printValue {
Class myInternalClass = NSClassFromString(#"MyPackageNameIfAny.MyInternalClass");
id myInternalClassInstance = [myInternalClass new];
int value = [myInternalClassInstance performSelector:#selector(internalProperty)];
NSLog(#"Value is %d ", value); // "value is 42"
}
#end
Using the SWIFT_CLASS macro and #objc class attribute could easily lead to errors when archiving. This approach is safer.

'Use of Unresolved Identifier' in Swift

So I have been making an app, and everything has been working great. But today I made a new class like usual and for some reason in this class I can't access Public/Global variable from other classes. All the other classes can, but now when ever I try to make a new class I can't. How would this be fixed?
I am using Swift and Xcode 6.
Working Class:
import UIKit
import Foundation
import Parse
import CoreData
var signedIn = true
class ViewController: UIViewController {
New Class:
import UIKit
class NewClass: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
signedIn = false
}
But on signedIn = false
I get the error:
use of unresolved identifier "signedIn"
One possible issue is that your new class has a different Target or different Targets from the other one.
For example, it might have a testing target while the other one doesn't. For this specific case, you have to include all of your classes in the testing target or none of them.
Once I had this problem after renaming a file. I renamed the file from within Xcode, but afterwards Xcode couldn't find the function in the file. Even a clean rebuild didn't fix the problem, but closing and then re-opening the project got the build to work.
'Use of Unresolved Identifier' in Swift my also happen when you forgot to import a library. For example I have the error:
In which I forgot the UIKit
import UIKit
Sometimes the compiler gets confused about the syntax in your class. This happens a lot if you paste in source from somewhere else.
Try reducing the "unresolved" source file down to the bare minimum, cleaning and building. Once it builds successfully add all the complexity back to your class.
This has made it go away for me when re-starting Xcode did not work.
Another place I've seen this error is when your project has multiple targets AND multiple bridging headers. If it's a shared class, make sure you add the resource to all bridging headers.
A good tip to is to look in the left Issue Navigator panel; the root object will show the target that is issuing the complaint.
For me this error happened because I was trying to call a nested function. Only thing I had to do to have it fixed was to bring the function out to a scope where it was visible.
In my case, I had an Object-C file which was also in the same Target Membership. I fixed by adding #import "YourObjectCFileHeader.h" inside file Bridging-Header.h
Because you haven't declared it. If you want to use a variable of another class you must use
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController : ViewController = segue.destinationViewController as ViewController
DestViewController.signedIn = false
}
You have to put this code at the end of the NewClass code
Your NewClass inherits from UIViewController. You declared signedIn in ViewController. If you want NewClass to be able to identify that variable it will have to be declared in a class that your NewClass inherits from.
I got this error for Mantle Framework in my Objective-Swift Project.
What i tried is ,
Check if import is there in Bridging-Header.h file
Change the Target Membership for Framework in Mantle.h file as shown in below screenshot.
Toggle between Private Membership first build the project , end up with errors.
Then build the project with Public Membership for all the frameworks appeared for Mantle.h file, you must get success.
It is just a bug of building with multiple framework in Objective-C Swift project.
If this is regarding a class you created, be sure that the class is not nested.
F.e
A.swift
class A {
class ARelated {
}
}
calling var b = ARelated() will give 'Use of unresolved identifier: ARelated'.
You can either:
1) separate the classes if wanted on the same file:
A.swift
class A {
}
class ARelated {
}
2) Maintain your same structure and use the enclosing class to get to the subclass:
var b = A.ARelated
I did a stupid mistake. I forgot to mention the class as public or open while updating code in cocoapod workspace.
Please do check whether accesor if working in separate workspace.
You forgot to declare the variable. Just put var in front of signedIn = false
My issue was calling my program with the same name as one of its cocoapods. It caused a conflict. Solution: Create a program different name.
This is not directly to your code sample, but in general about the error. I'm writing it here, because Google directs this error to this question, so it may be useful for the other devs.
Another use case when you can receive such error is when you're adding a selector to method in another class, eg:
private class MockTextFieldTarget {
private(set) var didCallDoneAction = false
#objc func doneActionHandler() {
didCallDoneAction = true
}
}
And then in another class:
final class UITextFieldTests: XCTestCase {
func testDummyCode() {
let mockTarget = MockTextFieldTarget()
UIBarButtonItem(barButtonSystemItem: .cancel, target: mockTarget, action: MockTextFieldTarget.doneActionHandler)
// ... do something ...
}
}
If in the last line you'd simply call #selector(cancelActionHandler) instead of #selector(MockTextFieldTarget.cancelActionHandler), you'd get
use of unresolved identifier
error.

TinyIoC, Xamarin.iOS, linker settings

I'm trying to get TinyIoC working on Xamarin.iOS, but I'm not having a lot of luck. My project linker settings are set to "Link SDK assemblies only".
I'm literally doing something this simple:
public interface IPerson { int age { get; } }
public class Person : IPerson { public int age { get { return 99; } } }
Then my registration code looks like this (I've just placed it in my AppDelegate in a toy app):
TinyIoCContainer.Current.Register<IPerson,Person>.AsMultiInstance();
When I attempt to grab an IPerson, I get a runtime exception saying that IPerson cannot be resolved (this code is found immediately after the registration code in the AppDelegate of the toy app):
IPerson person = TinyIoCContainer.Current.Resolve<IPerson>();
Here's the error:
Unable to resolve type: TinyTest.IPerson
If, however, I change the linker settings to "Don't link", everything works fine. This is obviously untenable, though, because the binary becomes enormous.
I've tried placing [Preserve] attributes on the IPerson interface and the Person class, but no dice. I also tried just manually declaring a variable of type IPerson and instantiating it with a new Person() and then grabbing the age property, just to make sure the type was included in the build, but no luck there either.
Feel like I'm missing something here - can someone point me in the right direction?
Thank you!
This is a bug because reflection is used to call an internal Expression<TDelegate> constructor.
The linker cannot analyze reflection usage (it's beyond static analysis) so it must be aware of those special cases.
This is obviously untenable, though, because the binary becomes enormous.
Keep using the default Link SDK option but add the --linkskip=System.Core to your Additional mtouch arguments, inside your Project Options, iOS Build.
That way only System.Core (from the SDK) will not be linked and the increase in size will be much smaller. Of course this is only a workaround until a new version fix the issue properly.

Resources