Swift 3 How to manage different delegates for different Targets - ios

We have created two Targets (Target_One & Target_Two) for same project.
Target_One contain SDK1 and delegate Target1SDKHelperDelegate
Target_Two contain SDK2 and delegate Target2SDKHelperDelegate
The reason to create two target : we need to upload two apps with same UI but different SDK integration.
As we know, each SDK has their own delegates. So we want to apply delegates specific to the target.
Example: Target_One has a class named MyClass
class MyClass: NSObject, Target1SDKHelperDelegate {
}
In above class, we have implemented Target1SDKHelperDelegate delegate.
We are using same class for Target_Two also and we want to use Target2SDKHelperDelegate for Target_Two.
So how we can put two different delegates to for two different targets?
We also know to manage target we should use below code.
#if Target_One
#else
#endif
But anyone tell us how to manage delegate by using above?
We want to do something like :
class MyClass: NSObject
#if Target_One
, Target1SDKHelperDelegate
#else
, Target2SDKHelperDelegate
#endif
{
}

Can try like this code with use typealias:
#if FREE_VERSION
public typealias DELEGATES = UIViewController & AttributeUDBClientMainDelegate & AttributeSubscriptionHelperDelegate
#else
public typealias DELEGATES = UIViewController
#endif
public final class SettingsViewController: DELEGATES {

Actually it is pretty simple and direct . Lately I faced the issue cause of size of iPA went too big like 20MB . I disabled not needed features from some targets and had to manage the shared files like Appdelegate when it has references to disabled features files . Solved this by simply duplicating Appdelegate file and put it in a specific paths related to specific targets . Then included each appDelegate file under its target. It worked . The idea is the same like if you have firebase push notifications pLists configuration files for multiple targets OR imageAssets folders. Hope this helps.

Related

{Module_name}-Swift.h file not working well only in Swift 4 projects unlike Swift 3.2

Anyone faced problems in {Module_name}-Swift.h file for Swift 4 projects? I've noticed -Swift.h autogenerated file not working well with Swift 4 syntax unlike Swift 3.2!.
For example, -Swift.h file doesn't contain all variables and methods which implemented in the custom Swift classes which inherited from NSObject class!
I've used #objc and #classkeywords but no way.
I don't get any errors! the problem is if I've created a class like this:
import Foundation
class Utils: NSObject {
let abc: String?
func xyz() {
print("")
}
}
and navigate to {Module_name}-Swift.h I see something like that:
SWIFT_CLASS("_TtC3{Module_name}5Utils")
#interface Utils : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Problem
Both let abc: String? and func xyz() have been never included in {Modue_name}-Swift.hfile!
I think in Swift 4 you have to mark a lot more things #objc (nothing implicit anymore) but other than that it should just be in there.
You can all check it to confirm class name in .h file like:
#class filename;
The generated file {Module}-Swift.h does not contain your variables and methods, the file is generated to give you access to the Module namespace.
The actual interface for the generated module lives in Module.swiftmodule/arm64.swiftmodule (depending on built architecture).
More information on its contents:
https://bugs.swift.org/browse/SR-2502
https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160111/000827.html
https://stackoverflow.com/a/24396175/1755720
however... the format is not documented anywhere and is subject to change. A good starting point would be to look in include/swift/Serialization/ModuleFormat.h
As to why it's not working - Swift 4 has a migration process, please ensure you have followed it: https://swift.org/migration-guide-swift4/
Xcode will pick up most things ... but it won't get everything!
And why do you need header files for Swift classes? You just can mark swift class as #objc and you will be able to reach all its properties.

I've tried it all, but I can't import my Swift file to my Objective-C file

I've read though multiple threads on this on StackOverflow, and I can't seem to find the answer to my problem. My folder hierarchy is like this:
"Project Name"
- "Helpers"
"FileIWantToImport.swift"
"FileIWantToImportTo.m"
I have:
Set the build settings to have a Product Module Name FMB
Set Defines Modules to YES
a bridging header created by Xcode
cleaned my build
#import FMB-swift.h in my AppDelegate with out any problems
Added #objc to my class declaration in my file so it says #objc class MyClass: NSObject
HOWEVER. I cannot, for the life me, figure out why it won't import into FileIWantToImportTo.m
I have tried:
#import "FMB-swift.h"
#import "Helpers/FMB-swift.h"
#import "Product Name/Helpers/FMB-swift.h"
#import <FMB-swift.h>
#import <Helpers/FMB-swift.h>
#import <Product Name/Helpers/FMB-swift.h>
All I get is "file not found" error. Can someone please point me in the right direction? I've looked through all the other threads and the apple docs and can't figure it out. I'm running in Xcode 7 and 10.11 GMs if that makes a difference.
EDIT: Here is an example project https://github.com/Aghassi/Example-Project
This is what you need to do:
Create a Swift file in your ObjC based project. No need to create bridging headers, since they're used to see ObjC code from swift, not swift code from ObjC.
In your Target, under Build Settings set Embedded Content Contains Swift Code to Yes
Your Swift file should have an #objc public class.
#objc public class Example: NSObject {
public func printSomething(text: String) {
print(text);
}
}
Check your Project's name, and in your .m file, add #import <ProjectsName-Swift.h>, and you should be able to instantiate your class defined in the Swift file, and all it's public methods. You might need to build your project before importing the -Swift.h file.
Here's an example project with an ObjC class using Swift code: https://github.com/lucaslt89/Example-Project.git

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.

How to check if class is available or not in Static lib?

I will try to explain my problem.
I am creating two libs home.a & room.a independently. From home lib I have calls to the functions which I implemented in room.a
I am want two use this two libs in one project, the case is I want to keep room.a as optional. If I don't add room.a in project, I am not able to build project.
Error is:
Undefined symbols for architecture
"_RoomViewController", referenced from:
-[ParentViewController openView:] in home.a
Here RoomViewController is class from room.a & ParentViewController is class from home.a
I want to add condition in code home.a to check RoomViewController is present then create a object of RoomViewController.
Please suggest me a way for to do this.
Thanks in advance.
If you want the project to compile without errors, you need to add a header file that declares the RoomViewController class. For instance, write a RoomViewController+Private.h file.
#interface RoomViewController: UIViewController
#end
#interface RoomViewController()
//List of methods you want to use
- (void)methodA;
- (void)methodB;
#end
To check whether you linked the library room.a at runtime, you need to do the following:
if ([RoomViewController class]) {
// class exists
RoomViewController *instance = [[RoomViewController alloc] init];
} else {
// class doesn't exist
}

Resources