Using GTM v5 TAGCustomFunction in Swift - ios

I'm integrating GTM v5 (GTM + Firebase) in a Swift project, and I want to be able to call some methods when tags are triggered. However, it doesn't seem to work with Swift, although similar implementations in Objective C and Android projects did work.
Here's the class conforming to the TAGCustomFunction protocol :
import Foundation
import GoogleTagManager
final class Tags: NSObject, TAGCustomFunction {
func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}
Everything is working well, even though I see these kind of logs:
GoogleTagManager info: Processing logged event: applicationStart with parameters: (null)
But the log I'm printing is not showing…
I'm sure about the configuration of the container since this one is correctly loaded, and I use the exact same container for my Objective C project, in which it works perfectly.

I think TAGCustomFunction needs to have class/method #objc annotations, and the class-level #objc annotation needs to specify the class name, i.e.
import Foundation
import GoogleTagManager
#objc(Tags)
final class Tags: NSObject, TAGCustomFunction {
#objc func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}

After reading this SO post, I suddenly remembered that a Swift project could embed some objective C classes.
The solution to my problem was ridiculously easy to set up once I realized that, eventhough I've never had to do it before.
I created a new Cocoa Touch Class like the following :
Here is the .m :
#import "MyCustomTagClass.h"
#import "MySwiftClass-Swift.h"
#implementation MyCustomTagClass
- (NSObject*)executeWithParameters:(NSDictionary*)parameters {
[MySwiftClass myMethod];
}
#end
And here is the .h :
#import <Foundation/Foundation.h>
#import <GoogleTagManager/TAGCustomFunction.h>
#interface MyCustomTagClass : NSObject <TAGCustomFunction>
- (NSObject*)executeWithParameters:(NSDictionary*)parameters;
#end
Note that I import a header for my Swift class, which is automatically generated by Xcode. Just add -Swift.h after the name of your class to import it, just as I did in the .m example above.
Last, but not least, update your Swift class with #objc annotations at class and method declaration lines :
import Foundation
#objc class MySwiftClass: NSObject {
//...
#objc static func myMethod() {
// do something...
}
}
I hope this helped !

Related

How to access a method in a class written in swift using the app delegate

I am a newbie for objc. I have written a class exposing it to the JS side using swift. The content in that file looks like as follows,
import Foundation
#objc(AppLinkModule)
class AppLinkModule: NSObject{
#objc
static var appLink: String?
#objc
func setLink(link: String){
AppLinkModule.appLink = link;
}
#objc
func getLink(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
resolve(AppLinkModule.appLink);
AppLinkModule.appLink = nil;
}
}
The AppLinkModule.m file is as follows,
#import "React/RCTBridgeModule.h"
#interface RCT_EXTERN_MODULE(AppLinkModule, NSObject)
RCT_EXTERN_METHOD(getLink)
#end
What I actually want is to call the setLink method from the AppDelegate.m file or else directly set to the static variable appLink. Some instructions to achieve this will be greatly appreciated.
This looks fine.
If you want to call getLink() from your App Delegate, you'll need to import the Auto-generated Swift Bridging Header File that Xcode creates when you compile your code. This will allow the Objective-c AppDelegate.m file to find your exposed swift code.
#import <ProjectName-Swift.h> will allow your Swift to be exposed to the AppDelegate, and you already have marked the class and functions #objc so they will be visible.
Then you just need to either create a singleton or shared instance of your AppLinkModule (React Native Modules are all run statically) or you need to instantiate your module class.
Once you've done so, you can call this function in the AppDelegate like so:
[[[AppLinkModule ] shared ] getLink];

How to call Swift from an Objective C project App Delegate?

This code is from a Swift project App delegate. It is used to help configure Stripe with a publishable key.
//Appdelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
//The code helps configure Stripe with a publishable key.
STPPaymentConfiguration.shared().publishableKey = Constants.publishableKey
...
}
Two errors are displayed when building the app after adding the Swift line to the Objective C App Delegate
//AppDelegate.h
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
STPPaymentConfiguration.shared().publishableKey = Constants.publishableKey
Property 'shared' not found on object of type 'STPPaymentConfiguration'
Use of undeclared identifier 'Constants'
This was a similar error in compiling before #objc was added to the demo Swift function, MockApiClient. Should it be added elsewhere? I've tried adding #objc to the enum as mentioned in the answer here to no avail yet.
//Constants.swift
//This is the file the original Swift app delegate accesses
import Foundation
enum Constants {
static let publishableKey = "pk_live_..."
static let baseURLString = "http://54.33.123.227:1234"
static let defaultCurrency = "usd"
static let defaultDescription = "Receipt" //change to describe actual app & charge
}
Steps taken:
Opened the Objective C project and created a bridging header
Created a demo class in Swift while still in the Obj C project to make sure it can be used, in this case to print from an Objective C file when the view is loaded. Specifically derived from an NSObject. Adding the override to the initializer and using the #objc prefix.
// MockApiClient.swift
import Foundation
class MockApiClient: NSObject
{
override init()
{
print("Initializer called in Mock API client")
}
#objc func executeRequest()
{
print("The execute request has been called in the Mock API Client")
}
}
//ViewController.h
//Prints the Swift request written in the MockApiClient the the view loads
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MockApiClient *client = [MockApiClient new];
[client executeRequest];
}
Copied the #import "ViewController.h" import to the automatically generated project-Bridging-Header.h file to expose the Objective C in it to swift
Added the necessary Swift files to the Objective C project so that the Constants.publishablekey data from Constants.swift can be found
How can this Swift App delegate code be added to the App delegate of an Objective C project?
Edit: error when adding #objc to the enum declaration in Constants.swift
Edit: error when adding #objc to the enum declaration in Constants.swift
Swift enums used as namespace cannot be exposed to Objective-C.
You may need to use class to make it work both for Swift and Objective-C:
#objcMembers
class Constants: NSObject {
static let publishableKey = "pk_live_..."
static let baseURLString = "http://54.33.123.227:1234"
static let defaultCurrency = "usd"
static let defaultDescription = "Receipt" //change to describe actual app & charge
private override init() {}
}
The ability of Objective-C to see things defined in Swift depends on the automatically generated header file. This is not the bridging header. It is a header file buried in your derived data called YourProject-Swift.h. Your Objective-C .m file needs to #import "YourProject-Swift.h" (using the correct name).
Then, your Swift things need to get into that file. For that to happen, they need to be of a type that Objective-C can see at all (i.e. classes) and they need to be explicitly exposed to Objective-C with appropriate #objc attributes.

Is it possible to call objective C application delegate method from Swift class of the same project?

I'm merging my Swift project with already existing Objective-C code. I need to call some important methods of Swift class from objective C app delegate. I tried all methods given in net, but it was no use. Can any one help me out?
Yes, it's possible but with some limitations.
You can use only classes which inherited from NSObject, with public attribute and marked with #objc. At Objective-C code you should import "ProductModuleName-Swift.h" file which generated by compiler.
Here is an example of Swift class:
import Foundation
#objc public class ExampleClass: NSObject {
#objc public var someInstanceProperty = "Property"
#objc public func someFunction() {
print("Some function")
}
}
Notice that this class inherited from NSObject and have #objc and public attributes. After command+B you can take a look at generated bridge header through Assistance editor:
Then you should import the bridge header at your Objective-C class.
#import "ProductModuleName-Swift.h"
And then you can use your Swift class at Objective-C code like any other Objective-C class:
__auto_type const someClass = [ExampleClass new];
[someClass someFunction];
NSLog(#"%#", someClass.someInstanceProperty);
Here is an additional information from Apple:
Importing Swift into Objective-C

Cannot find protocol declaration for 'AVCapturePhotoCaptureDelegate'

I'm using a Swift file, a sub-class of UIViewController in a project which is basically built previously in Objective-C. The bridging-header file is added by Xcode. I'm getting following error:
Cannot find protocol declaration for 'AVCapturePhotoCaptureDelegate'
This is my VC:
import UIKit
import AVFoundation
class ScanTargetAVFViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ScanTargetAVFViewController: AVCapturePhotoCaptureDelegate {
}
EDIT:
I've tried removing #import "MyProject-Swift.h" from one of the objective-c file and it works now. However, I need to reference Swift code from my Objective-C files. Any clue?
Try importing the MyProject-Swift.h file into MyObjectiveC.m rather than MyObjectiveC.h. If there are Swift classes you need to reference in MyObjectiveC.h you can use the class directive.
#class MySwiftClass;

Objective-C protocol method invisible in Swift

I'm currently working on some Swift classes in my ObjC project.
The problem I have is the following:
I have this protocol declared in ClassA.h:
#protocol MyProtocol <NSObject>
- (void)complexMethodWithArg1:(id)arg1 arg2:(id)arg2 arg3:(id)arg3;
- (Folder *)currentDestinationFolder;
- (Flow)currentFlow;
#end
Pretty standard stuff.
Now my goal is to have a swift class with a property that is an object implementing this protocol.
So naturally, I add my class to the swift bridging header:
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "ClassA.h"
and declare my property in my swift file under ClassB which is a UIViewController that implement ANOTHER protocol
class ClassB : UIViewController, AnotherProtocol {
var delegate:MyProtocol?
}
Problem here is: I want to call a bunch of my delegate methods in viewDidLoad. It's working for all of them except ONE method that gets not autocompletion and errors the compilation if entered manually:
override func viewDidLoad() {
self.delegate?.currentDestinationFolder() // works great, no problem
self.delegate?.currentFlow() // works great, no problem
self.delegate?.complexMethodWithArg1(arg1: arg1, arg2: arg2, arg3: arg3) // PROBLEM : no autocompletion, error if entered manually !
super.viewDidLoad()
}
I have no idea what's going on, it's not related to optional or required protocol methods, not related to the fact that my delegate property is optional (tried unwrapped).
Has anybody face some similar issue? seems like some kind of bug?
I went ahead and tried to reproduce the problem on an empty project.
MyProtocol.h (taking the declaration from your question and comments)
#import Foundation;
#import UIKit;
#class CAPNavigationBar;
#protocol MyProtocol <NSObject>
- (void)setupNavigationItemInNavigationBar:(CAPNavigationBar *)navigationBar
navigationItem:(UINavigationItem *)navigationItem
inViewController:(UIViewController *)viewController;
#end
CAPNavigationBar.h (just a mock)
#import Foundation;
#interface CAPNavigationBar : NSObject
#end
ViewController.swift
import UIKit
class ViewController: UIViewController {
var delegate: MyProtocol?
override func viewDidLoad() {
super.viewDidLoad()
let capNavigationBar = CAPNavigationBar()
self.delegate?.setupNavigationItemInNavigationBar(capNavigationBar, navigationItem: nil, inViewController: self)
}
}
Bridging header
#import "MyProtocol.h"
#import "CAPNavigationBar.h"
Summary
Everything is working as expected.
You have either a simple typo somewhere or you are not importing correctly all the types into Swift. Especially make sure that you are not importing types only as forward declarations.

Resources