i'm trying to get the Objection Framework working with Swift (XCode 6.4).
Everything works pretty well beside the macros required for register/inject objects e.g. objection_register
I followed the approach from "Bridging Cocoalumerjack with Swift" to get the macros working with Swift but XCode always complain: Use of undeclared identifier 'initialize' when implementing objectionRegister function in ObjectionSwift.m. Since i'm not to familiar with objective-c i got stuck when looking at the Objection.h initializer methods and trying to figure out whats wrong
Thx for your help!
ObjectionSwift.h
#import <Foundation/Foundation.h>
#interface ObjectionSwift : NSObject
+ (void) objectionRegister(NSString *) name;
#endif
ObjectionSwift.m
#import "ObjectionSwift.h"
#import "Objection.h"
#implementation ObjectionSwift
+ (void) objectionRegister:(NSString *) name {
objection_register_singleton(name)
}
#end
UPDATE
I switched to Typhoon as DI Framework which provides Swift support an works pretty well.
You can use class variable:
class ListingsViewController: UIViewController
{
class var initialize: Bool
{
JSObjection.registerClass(ListingsViewController.self, scope: JSObjectionScopeNormal)
return true
}
}
Related
I'm trying to add a webshop to my app. The problem is that the webshop has provided me with an Obj-C framework and I'm only familiar with making iOS apps in Swift.
I've managed to setup an Obj-C bridging header and instantiate the webshop object.
My project looks like this:
When the shop button on my CodeDetailViewController gets tapped this function gets triggered:
#IBAction func shopButtonPressed(sender: UIButton){
let instanceOfShop : Shop = Shop()
instanceOfShop.showShop()
}
My Shop.m looks like this:
#import <Foundation/Foundation.h>
#import <FMShop/FMShop.h>
#import "Shop.h"
#implementation Shop
- (void) showShop {
//SANDBOX
FMShop* shop = [[FMShop alloc] initWithKey:#"4M7HDPAQMY68S"
storeKey:#"C8RSHBH710GK8"
buyerID:nil
sandbox:YES
controller:nil
delegate:nil];
[shop showStore:kShopViewHome ID:nil];
}
#end
The showShop function in this class gets triggered. Nothing however shows up.
The manual on how to implement this webshop is aimed at Obj-C apps. Concerning the delegate it states:
delegate: Please define a class or an interface that implements the delegates. Without such, the Store will not show. Please go to FMShopDelegate to see the supported list of methods and callbacks.
This is what I could find in the framework:
#protocol FMShopDelegate <NSObject>
#optional
-(void)shopWillAppear:(FMShop*)shop;
-(void)shopDidAppear:(FMShop*)shop;
-(void)shopWillDisappear:(FMShop*)shop;
-(void)shopDidDisappear:(FMShop*)shop;
-(void)shopProductInfo:(FMShop*)shop products:(NSArray*)productArray;
-(void)shopHistoryOrderInfo:(FMShop*)shop orders:(NSArray*)orderArray;
-(void)shopPlaceOrderSuccess:(FMShop*)shop orderID:(NSString*)orderID;
-(void)shopOrderUserCancelled:(FMShop*)shop orderID:(NSString*)orderID;
-(void)shopFailed:(FMShop*)shop error:(FMShopErrorType)error;
#end
Can anyone tell me how to connect this final method to the right delegate?
Sounds like if you don't provide a delegate when you instantiate your instance of FMShop, then the show method will not work. In your Shop.m file, you're instantiating the FMShop object with nil as the delegate parameter.
Try having an object conform to the FMShopDelegate protocol, and passing this object as the delegate parameter. You could even have your Shop object conform to the protocol itself, and pass self as the delegate if that would serve your purposes.
You could do this in Swift in a new Shop.swift file, or in the Shop.m file in Objective-C:
Swift:
class Shop: NSObject {
func showShop() {
// I'm guessing on the syntax here, Xcode will help autocomplete the correct functions
let shop = FMShop(key: KEY, storeKey: STOREKEY...)
shop.show(store: kShopViewHome, id: nil)
}
}
extension Shop: FMShopDelegate {
// Implement any of the protocol functions you want here
}
Objective-C:
#interface Shop() <FMShopDelegate> // Conform to the delegate here
#end
#implementation Shop
- (void) showShop {
//SANDBOX
FMShop* shop = [[FMShop alloc] initWithKey:#"4M7HDPAQMY68S"
storeKey:#"C8RSHBH710GK8"
buyerID:nil
sandbox:YES
controller:nil
delegate:self];
[shop showStore:kShopViewHome ID:nil];
}
// Implement any of the protocol methods here
#end
You have to use bridging header for accessing obj-c file in swift
Below is the apple doc to describe how to create & use bridging header
Apple Doc
Use bridging header in your project.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
In one of my projects I want to use a Swift class from an Objective C class. I've created XXX-swift.h file and a bridging header file. Most swift files are working perfectly, but I found a problem of an APiResult class of mine.
#objc class ApiResult:NSObject {
var success:Bool?
var message:String?
}
and in XXX-swift.h file
SWIFT_CLASS("_TtC16RemoteController9ApiResult")
#interface ApiResult : NSObject
#property (nonatomic, copy) NSString * _Nullable message;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
there the success property is missing. A rebuild & clean build didn't work.
Why is this happening
Objective-C doesn't support optionals. The bridging from Swift to Objective-C allows optionals for class types because those just translate to nil on the ObjC side. However, Bool is a primitive type, and there's no such thing as a nil boolean value in Objective-C.
I am inexperienced with Objective-C and this is my first attempt at anything Swift. I Can't get Xcode to compile. ( I may also be missing a necessary line or two, but if I get it to compile, I can usually figure out the rest )
I've been all over docs and getting started, I don't know enough to know the answer if it's there:
http://bustoutsolutions.github.io/siesta/guide/objc/
https://bustoutsolutions.github.io/siesta/api/
My Code:
// MyAPI.swift
import Siesta
import SwiftyJSON
#objc class MyAPI: Service {
internal static let instance = MyAPI(baseURL:"https://api.example.com")
}
// ViewController.mm
#import "ViewController.h"
#import "PlateScanner.h"
#import "Plate.h"
#import "MyProject-swift.h"
...
- (IBAction)callAPI:(id)sender
{
[[MyAPI.instance resource:#"/profile/"] child:#"123"];
}
-(void)resourceChanged: (BOSResource*) resource event: (NSString*) event {
if([event hasPrefix:#"NewData"]) {
NSLog(#"%#",[resource text]);
}
}
...
I get the following 2 errors from ViewController.mm:
1. line starting "[[MyAPI": No visible #interface for 'MyAPI' declares the selector 'resource:'
2. line starting "-(void)reso": Expected a type
From this I understand that my ViewController is not understanding the necessary BOSResource type, but I don't know how to get the swift library imported properly?
Xcode generated code:
//MyProject-swift.h
...
SWIFT_CLASS("_TtC10MyProject5MyAPI")
#interface MyAPI : BOSService
+ (MyAPI * _Nonnull)instance;
#end
...
With Error in line starting "#interf...": Cannot find interface declaration for 'BOSService', superclass of 'MyAPI'
Any help is greatly appreciated!
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.
I started to build an IOS app with the new programming language Swift. I managed to use CocoaPods and was able to successfully create the DDTTYLogger with my CustomLoggerFormatter (Objective-C) in my AppDelegate.swift and append it to the loggers.
var customLoggerFormatter = CustomLoggerFormatter()
var consoleLogger: DDTTYLogger = DDTTYLogger.sharedInstance()
consoleLogger.setLogFormatter(customLoggerFormatter)
DDLog.addLogger(consoleLogger)
But the problem is, that the CocoaLumberjack Library is using preprocessor macros for the logger methods like DDLogVerbose(#"..")
Which is defined in the DDLog.h:
#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
Is there any workaround to make preprocessor defines work in Swift? Or did anyone try something similar with more success?
Okay, I just found a solution. Writing an Objective-C Wrapper class calling the preprocessors and offering methods to call it.
Hopefully this will help other people facing the same issues.
I first created a header file:
#interface DDLogWrapper : NSObject
+ (void) logVerbose:(NSString *)message;
+ (void) logError:(NSString *)message;
+ (void) logInfo:(NSString *)message;
#end
With the corresponding implementation:
#import <Foundation/Foundation.h>
#import "DDLogWrapper.h"
// Logging Framework Lumberjack
#import "DDLog.h"
#import "DDASLLogger.h"
#import "DDTTYLogger.h"
// Definition of the current log level
#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif
#implementation DDLogWrapper
+ (void) logVerbose:(NSString *)message {
DDLogVerbose(message);
}
+ (void) logError:(NSString *)message {
DDLogError(message);
}
+ (void) logInfo:(NSString *)message {
DDLogInfo(message);
}
#end
Important is to add the DDLogWrapper.h File to the ProjectName-Bridging-Header.h file and then you are able to instantiate in Swift the DDLogWrapper and call the methods logVerbose, logError, logInfo..
With the following code I was able to make a log statement:
DDLogWrapper.logVerbose("TEST");
I created a Swift wrapper for CocoaLumberjack that encapsulates everything nicely.
DDLog.addLogger(DDTTYLogger.sharedInstance())
DDLog.logLevel = .Info
logInfo("Info")
logWarn("Warn")
logDebug("Debug")
logError("Error")
As of 2.0.0beta4, CocoaLumberJack includes a CocoaLumberJack.swift file that makes its integration with Swift projects really easily.
They use a global var defaultDebugLevel to set the DDLogLevel, and you can swift basic precompile macros to customize it to your needs.
#if DEBUG
defaultDebugLevel = DDLogLevel.All
#else
defaultDebugLevel = DDLogLevel.Warning
#endif
DDLog.addLogger(DDTTYLogger.sharedInstance())
DDLogDebug("Debug")
DDLogInfo("Info")
DDLogWarn("Warning")
DDLogVerbose("Verbose")
DDLogError("Error")