I have two files, one that's called CodeInjection.m with the content like
#import <Foundation/Foundation.h>
#import "Someheaderfile.h"
#interface CodeInjection: NSObject
#end
#implementation CodeInjection
static void __attribute__((constructor)) initialize(void){
NSLog(#"==== Code Injection in Action====");
[[CodeInjectionSwift shared] performTask];
}
#end
Then I have a CodeInjectionSwift.swift file with content
import Foundation
import NetworkInterceptor
#objc class CodeInjectionSwift: NSObject {
#objc public static let shared = CodeInjectionSwift()
override private init(){}
#objc func performTask(){
let requestSniffers: [RequestSniffer] = [
RequestSniffer(requestEvaluator: AnyHttpRequestEvaluator(), handlers: [
SniffableRequestHandlerRegistrable.console(logginMode: .nslog).requestHandler()
])
]
let requestRedirectors: [RequestRedirector] = [
RequestRedirector(requestEvaluator: DomainHttpRequestEvaluator(domain: "www.antennahouse.com"), redirectableRequestHandler: AlternateUrlRequestRedirector(url: URL(string: "https://www.rhodeshouse.ox.ac.uk/media/1002/sample-pdf-file.pdf")!))
]
let networkConfig = NetworkInterceptorConfig(requestSniffers: requestSniffers,
requestRedirectors: requestRedirectors)
NetworkInterceptor.shared.setup(config: networkConfig)
NetworkInterceptor.shared.startRecording()
}
}
When trying to use "CodeInjectionSwift", I get errors like this
Make #objc func performTask() public:
#objc public func performTask()
My original answer is wrong. I've left it below for context.
In fact the whole problem is that you need to import a special header into your Objective-C class.
#import "TestImportSwift-Swift.h"
where TestImportSwift is replaced by the name of your project.
Old wrong answer
shared is not a class member, it is a static member. Unfortunately, Swift doesn't support class variables, only functions, so you could try
#objc class CodeInjectionSwift: NSObject {
static let _shared = CodeInjectionSwift()
#objc public class func shared() -> CodeInjectionSwift { return _shared }
// Rest of the class
Note that I haven't tried this in a real project.
Related
I am using FXForm Library and want to get data from my Swift file in Objective C file function.
Demo Project Link
Swift Code Implimnetation:
let fontName = "HelveticaNeue"
let fontSizeLarge:CGFloat = 14.0
var hiddenElementFromFormIndex = [Int]()
//fx form variables
#objc class FXFormVariables : NSObject {
public override init() {}
class func FXFontName() -> String { return fontName }
class func FXFontSize() -> CGFloat { return fontSizeLarge }
class func FXHiddenCell() -> NSArray { return hiddenElementFromFormIndex as NSArray }
}
In Objective C file, I am getting error when we write below like:
NSArray *hideArray = [FXFormVariables FXHiddenCell];
I created the bridge header file correctly and Target Membership is checked in the Objective C file.
Error I am getting:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$__TtCC13Social_Engine11AppDelegate15FXFormVariables", referenced from:
objc-class-ref in FXForms.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Architectures and Valid Architectures are:
armv7 and armv64
To use Swift class inside Objective-C file you need to import Xcode-generated header file in your Objective-C file.
#import "ProductModuleName-Swift.h"
I created a Test project with the same name.
Swift File:
//
// FXFormVariables.swift
// Test
//
// Created by Puneet Sharma2 on 12/07/17.
// Copyright © 2017 com.puneet.sh2525. All rights reserved.
//
import Foundation
import UIKit
let fontName = "HelveticaNeue"
let fontSizeLarge:CGFloat = 14.0
var hiddenElementFromFormIndex = [Int]()
//fx form variables
#objc class FXFormVariables : NSObject {
public override init() {}
class func FXFontName() -> String { return fontName }
class func FXFontSize() -> CGFloat { return fontSizeLarge }
class func FXHiddenCell() -> NSArray { return hiddenElementFromFormIndex as NSArray }
}
Objective-C File
#import "ABC.h"
#import "Test-Swift.h"
#implementation ABC
- (void)drawRect:(CGRect)rect {
NSArray *hideArray = [FXFormVariables FXHiddenCell];
}
You can read more about it here.
Literally I spent hours trying to figure out why this was happening with one of my swift files that i was importing into my Objc codebase, and the real reason was due to the use of a nested class.
Such as:
#objc public class MyClass {
#objc public class SecondClass {}
}
And if I tried using SecondClass in my objective-c code, I would the Apple Mach O-Link errors of death. So the solution was to de-nest the class you want to use.
#objc public class MyClass {}
#objc public class SecondClass {}
I wrote a Swift utility class in which I define several static methods and a private static constant. However, I wish this constant to be loaded immediately after the class is referenced for the first time, as opposed to lazily. The only thing I can think of doing is to reference the static constant in every single static function like this:
private static let myObserver: Observer = {
let observer = Observer(actionToPerform: foo1)
SomeOtherClass.register(observer)
return observer
}()
static func foo1() {
_ = myObserver
...
}
static func foo2() {
_ = myObserver
...
}
static func foo3() {
_ = myObserver
...
}
//even more of a hassle:
static let myIntConstant: Int = {
_ = myObserver
return 5
} ()
.
.
.
However, that solution looks pretty ugly. Is there a cleaner way? Some sort of class initialization callback I can use?
Ok, I seem to have found a workable solution to my own question.
Ensure that the class is a subclass of NSObject.
Insert the following code:
override class func initialize() {
_ = myObserver
}
After doing this, the static constant is loaded immediately after the class is referenced, as desired.
Of course, this approach is limited by the fact that the class must be a subclass of NSObject, which may not be possible for all such classes. Any other potential drawbacks to this approach would be welcomed!
I'm trying to implement a framework using swift but the header file is not been updated with the class or the functions:
Here is my code in my swift file:
public class one{
public func two(name:String) -> String {
print(name)
}
}
#objc public class SwiftInObjc:NSObject {
public func bla(){
}
}
After I build this is what I see in my header file:
#import <UIKit/UIKit.h>
FOUNDATION_EXPORT const unsigned char sampleFramework[];
framework using statements like #import <sampleFramework/PublicHeader.h>
How can generate update to the header file?
Here is my settings.swift file :
import Foundation
class Settings {
func setDefaultSettings() {
}
}
I would like to access the function in AppDelegate didFinishLaunchingWithOptions. I've tried calling
Settings.setDefaultSettings() but it didn't work.
You have two ways to make this work:
1.Initialise object of Settings class
Settings().setDefaultSettings()
or
let settings = Settings()
settings.setDefaultSettings()
2.Make the function static
static func setDefaultSettings(){...}
in this case you can call it your way
Settings.setDefaultSettings()
Method can be defined in two ways : Instance method and class Methods
You have declared the method as Instance method, which can accessible through the instance of the class. So, to access the methods needs instance of the class, which can be created as
let settingObj = Settings()
settingObj.setDefaultSettings()
-------------------------------------------------
//Combined statement
Settings().setDefaultSettings()
and class method can be written using the keyword static or class. They can be access by the class name itself. Please see the example below
class Settings {
class func setDefaultSettings()
{
print("Default Settings")
}
}
Now you can call the method directly by the class name
Settings.setDefaultSettings()
You can access this method as follow
1) Create one Class of NSObject as follow.(GLOBAL.swift)
class GLOBAL : NSObject {
//sharedInstance
static let sharedInstance = GLOBAL()
}
2) Define your method here.
class GLOBAL : NSObject {
//sharedInstance
static let sharedInstance = GLOBAL()
func setDefaultSettings() {
}
}
3) Use above method as follow.
GLOBAL.sharedInstance.setDefaultSettings()
You can also use this setDefaultSettings() in setting class and also any other class.
I'm looking for behavior similar to Objective-C's +(void)initialize class method, in that the method is called once when the class is initialized, and never again thereafter.
A simple class init () {} in a class closure would be really sleek! And obviously when we get to use "class vars" instead of "static vars in a struct closure", this will all match really well!
If you have an Objective-C class, it's easiest to just override +initialize. However, make sure subclasses of your class also override +initialize or else your class's +initialize may get called more than once! If you want, you can use dispatch_once() (mentioned below) to safeguard against multiple calls.
class MyView : UIView {
override class func initialize () {
// Do stuff
}
}
If you have a Swift class, the best you can get is dispatch_once() inside the init() statement.
private var once = dispatch_once_t()
class MyObject {
init () {
dispatch_once(&once) {
// Do stuff
}
}
}
This solution differs from +initialize (which is called the first time an Objective-C class is messaged) and thus isn't a true answer to the question. But it works good enough, IMO.
There is no type initializer in Swift.
“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
You could use a type property which default value is a closure. So the code in the closure would be executed when the type property (or class variable) is set.
class FirstClass {
class var someProperty = {
// you can init the class member with anything you like or perform any code
return SomeType
}()
}
But class stored properties not yet supported (tested in Xcode 8).
One answer is to use static, it is the same as class final.
Good link for that is
Setting a Default Property Value with a Closure or Function
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
Code example:
class FirstClass {
static let someProperty = {
() -> [Bool] in
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
print("setting default property value with a closure")
return temporaryBoard
}()
}
print("start")
FirstClass.someProperty
Prints
start
setting default property value with a closure
So it is lazy evaluated.
For #objc classes, class func initialize() definitely works, since +initialize is implemented by the Objective-C runtime. But for "native" Swift classes, you'll have to see the other answers.
You can use stored type properties instead of initialize method.
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
}
But since stored types properties are actually lazily initialized on their first access, you will need refer them somewhere. You can do this with ordinary stored property:
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
private let initializer: Void = SomeClass.initializer
}
#aleclarson nailed it, but as of recent Swift 4 you cannot directly override initialize. You still can achieve it with Objective-C and categories for classes inheriting from NSObject with a class / static swiftyInitialize method, which gets invoked from Objective-C in MyClass.m, which you include in compile sources alongside MyClass.swift:
# MyView.swift
import Foundation
public class MyView: UIView
{
#objc public static func swiftyInitialize() {
Swift.print("Rock 'n' roll!")
}
}
# MyView.m
#import "MyProject-Swift.h"
#implementation MyView (private)
+ (void)initialize { [self swiftyInitialize]; }
#end
If your class cannot inherit from NSObject and using +load instead of +initialize is a suitable fit, you can do something like this:
# MyClass.swift
import Foundation
public class MyClass
{
public static func load() {
Swift.print("Rock 'n' roll!")
}
}
public class MyClassObjC: NSObject
{
#objc public static func swiftyLoad() {
MyClass.load()
}
}
# MyClass.m
#import "MyProject-Swift.h"
#implementation MyClassObjC (private)
+ (void)load { [self swiftyLoad]; }
#end
There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
I can't find any valid use case to have something like +[initialize] in Swift. Maybe this explains way it does not exist
Why do we need +[initialize] in ObjC?
To initialize some global variable
static NSArray *array;
+ (void)initialize {
array = #[1,2,3];
}
which in Swift
struct Foo {
static let array = [1,2,3]
}
To do some hack
+ (void)initialize {
swizzle_methodImplementation()
}
which is not supported by Swift (I can't figure out how to do it for pure Swift class/struct/enum)