Extract notification userinfo in a mixed language project - objective-c

I am working on a mixed language project, combining both Objective C and Swift in XCode 6.
In this project, the Singleton (Objective C) class posts a notification which is then received by ViewController (Swift).
Singleton.h
#import <Foundation/Foundation.h>
NSString *const notificationString = #"notificationString";
#interface Singleton : NSObject
+ (id)sharedSingleton;
- (void)post;
#end
Singleton.m
#import "Singleton.h"
static Singleton *shared = nil;
#implementation Singleton
- (id)init {
self = [super init];
if (self) {
}
return self;
}
#pragma mark - Interface
+ (Singleton *)sharedSingleton {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
});
return shared;
}
- (void)post {
char bytes[5] = {5, 7, 9, 1, 3};
NSDictionary *objects = #{#"device":[NSData dataWithBytes:bytes length:5], #"step1":[NSNumber numberWithInt:4], #"step2":[NSNumber numberWithInt:7]};
[[NSNotificationCenter defaultCenter] postNotificationName:notificationString
object:self
userInfo:objects];
}
#end
Of course, in this mixed-language project, bridging header must be setup correctly (just by adding #import "Singleton.h" in it)
ViewController.swift
import UIKit
class ViewController: UIViewController {
let singleton = Singleton.sharedSingleton() as Singleton
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "action:", name: notificationString, object: nil)
singleton.post()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Notification
func action(notification: NSNotification) {
let userInfo = notification.userInfo as Dictionary<String, String> // things go wrong here?!
let hash = userInfo["device"]
let completed = userInfo["step1"]
let total = userInfo["step2"]
}
}
This makes no compilation error. However, at run time, XCode reports:
fatal error: dictionary cannot be bridged from Objective-C
notification.userInfo contains an NSDictionary built by NSSTring: NSData, NSSTring: NSNumber, NSSTring: NSNumber while this command let userInfo = notification.userInfo as Dictionary<String, String> is trying to convert to Dictionary<String, String>
Does this cause the fatal error?
In ViewController.swift, what should I do to "read" NSDictionary passed in notification.userInfo, sent from Singleton.m?
Thanks in advance

try doing this
let userInfo = notification.userInfo as Dictionary<String, AnyObject>
as you indicated, the userInfo dictionary contains NSData, NSNUmber for values.

Related

How do you call a singleton class method from Objective C in a Swift class?

I have the following objective C code which I need to get into a Swift class :
In Logger.m -
+ (Logger *) log
{
static Logger *sharedLog = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedLogger = [[self alloc] init];
});
return sharedLogger;
}
- (void) printString:(NSString *) s
{
NSLog(s)
}
Which is in Logger.h as -
+ (Logger *) log;
- (void) printString:(NSString *) s;
Now, I have this code bridged in to Swift Project - LoggerUserApp where I'm trying to use the above print method from the singleton log shared class method.
I've tried -
Logger.log().printString("String") // Compiler Error. Use Object Construction Logger()
Logger().log().printString("String") // Compiler Error.
Logger().log.printString("String") // Compiler Error.
Logger.printString("String") // This does not call log
Can someone tell me what might be going wrong?
If the Swift compiler mistakenly identifies a method as a class factory method, you can use the NS_SWIFT_NAME macro, passing the Swift signature of the method to have it imported correctly. For example:
+ (id)recordWithQuality:(double)quality NS_SWIFT_NAME(record(quality:));
so,your method should be this:
+ (Logger *) log NS_SWIFT_NAME(log());
I can't reproduce your example completely, but at first sight, this should work:
Logger.log().printString("String")
Since your Obj-C singleton is a function returning the singleton (hence Logger.log() will return your singleton.
But since in your example code I see Logger and then PALog I can't say if this is all you need.
There are some other errors in your example: sharedLog and than sharedLogger.
Here is an example that should work:
Logger.h
#interface Logger : NSObject
+ (Logger *) log;
- (void) printString:(NSString *) s;
#end
Logger.m
#import "Logger.h"
#implementation Logger
+ (Logger *) log
{
static Logger *sharedLog = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedLog = [[self alloc] init];
});
return sharedLog;
}
- (void) printString:(NSString *) s
{
NSLog(#"%#", s);
}
#end
...-Bridging-Header.h
#import "Logger.h"
ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Logger.log().printString("Hello")
}
}
In the Swift update, if the class method's name is similar to the class name, then it just takes that as a custom initializer. Now since there was no parameter in the method, it wouldn't come up as the default initializer would come up instead.
Hence, to solve this problem, I had to change the name of the class method log to something different like newLog... and it worked fine.
Logger.newLog().printString("")

init an NSNumber from enum element in mixed-language project

I have a mixed-language project, Objective C and Swift, in XCode 6.
Singleton.h
#import <Foundation/Foundation.h>
enum {
enum_A = 0,
enum_B,
enum_C,
enum_D,
enum_E,
enum_F,
enum_G,
} enums;
#interface Singleton : NSObject
+ (id)sharedSingleton;
#end
Singleton.m
// Nothing's special in this file
#import "Singleton.h"
static Singleton *shared = nil;
#implementation Singleton
- (id)init {
self = [super init];
if (self) {
}
return self;
}
#pragma mark - Interface
+ (Singleton *)sharedSingleton {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
});
return shared;
}
#end
ViewController.swift
import UIKit
class ViewController: UIViewController {
let singleton = Singleton.sharedSingleton() as Singleton
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let n = NSNumber(char: enum_E) // ERROR HERE!!!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
(Of course I had to setup bridging header file, having #import "Singleton.h" added).
The ERROR is:
Cannot invoke 'init' with an argument list of type '(char: Int)'
It's strange that Swift can still recognize enum_E (I see it colorized in blue) but still pops up this error.
I tried (char)enum_E but still no luck.
Do you have any ideas?
Thanks,
Okay, apparently there actually is a difference between enums created in Objective-C and Swift. I assumed there was no difference, thus I only tested my approach in a Swift Playground.
enum created in Swift
// UInt32 used to have the same underlying type in both examples
enum TestEnum : UInt32 {
case A, B, C
}
var x = NSNumber(unsignedInt: TestEnum.C.rawValue)
// x == 2
To get the raw value from an enum value in Swift, you have to explicitly transform the enum value into the raw value. This can be done by adding .rawValue to your enum value.
enum created in Objective-C
Objective-C
enum TestEnum {
A = 0,
B = 1,
C = 2
};
Swift
let x : TestEnum = C
var number = NSNumber(unsignedInt: C.value) // alternative: x.value
println("the number is \(number)")
// Outputs: the number is 2
The difference to the Swift enums seems to be that you have to use .value instead of .rawValue and you can not prefix them with the type. The raw type in this case is UInt32.
Tested in Xcode 6.1.1, iOS SDK 8.1, iOS Simulator 8.1

Set Delegate in a mixed language project

I have an XCode6 mixed-language project, combining Swift and Objective C.
I created a Swift-based SingleView application, then added 2 Objective-C files, having contents as below:
Singleton.h
#import <Foundation/Foundation.h>
#protocol SingletonDelegate <NSObject>
#optional
- (void)methodCalled;
#end
#interface Singleton : NSObject
#property (weak, nonatomic) id <SingletonDelegate> singletonDelegate;
+ (id)sharedSingleton;
- (void)method;
#end
Singleton.m
#import "Singleton.h"
static Singleton *shared = nil;
#implementation Singleton
- (id)init {
self = [super init];
if (self) {
}
return self;
}
#pragma mark - Interface
+ (Singleton *)sharedSingleton {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
});
return shared;
}
- (void)method {
[self.singletonDelegate methodCalled];
}
#end
After setting up bridging header file as XCode suggested, I added #import "Singleton.h" into it.
In ViewController.swift, I tried to set singletonDelegate but always failed:
import UIKit
class ViewController: UIViewController, SingletonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Singleton.sharedSingleton().singletonDelegate = self // FAILED HERE!!!
Singleton.sharedSingleton().method()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The error message is:
Cannot assign to the result of this expression
Could any one show me how to fix this? (I am new in integrating Objective-C into Swift project)
Thanks in advance,
Create a class variable. Then set its delegate.
let singleton: Singleton = Singleton.sharedSingleton()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
singleton.singletonDelegate = self
//Singleton.sharedSingleton().singletonDelegate = self // FAILED HERE!!!
//Singleton.sharedSingleton().method()
}
fun methodCalled() {
//This method gets called from the Singleton class through the delegate
}

Are local static variables released on dealloc in Objective-C / ARC?

I have a factory which handles singletons as follows
#implementation MyFactory
- (AudioEngine *)theAudioEngine
{
static AudioEngine *ae = nil;
if (ae == nil) {
ae = [[AudioEngine] alloc] init];
}
return ae;
}
#end
Are such static local variables released when the MyFactory instance is dealloc'ed?
No they are not released. You could however move the variable to the heap and have a class method to release it, which is itself called from some app-level closedown method:
static AudioEngine *_ae = nil;
#implementation MyFactory
- (AudioEngine *)theAudioEngine
{
if (_ae == nil) {
_ae = [[AudioEngine] alloc] init];
}
return _ae;
}
+ (void)cleanup
{
if (_ae != nil)
{
// No need to release when in ARC mode (thanks to #AndrewMadsen)
//[_ae release];
_ae = nil;
}
}
#end
As stated by #trojanfoe the answer is no, presumably because the compiler allocates a permanent space for the static var and being also a local var, only the method itself would ever have access to it and thus the ability to dealloc it (via = nil).
Another strategy which works presuming your factory is an instance object and not a static class:
#implementation MyFactory
{
AudioEngine *_audioEngine;
}
- (AudioEngine *)audioEngineSingleton
{
if (_audioEngine == nil) {
_audioEngine = [[AudioEngine alloc] init];
}
return _audioEngine;
}
#end
When your MyFactory instance dealloc's so will the ivars.

How do I implement an Objective-C singleton that is compatible with ARC?

How do I convert (or create) a singleton class that compiles and behaves correctly when using automatic reference counting (ARC) in Xcode 4.2?
In exactly the same way that you (should) have been doing it already:
+ (instancetype)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
if you want to create other instance as needed.do this:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
else,you should do this:
+ (id)allocWithZone:(NSZone *)zone
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
This is a version for ARC and non-ARC
How To use:
MySingletonClass.h
#interface MySingletonClass : NSObject
+(MySingletonClass *)sharedInstance;
#end
MySingletonClass.m
#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
#implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
#end
This is my pattern under ARC.
Satisfies new pattern using GCD and also satisfies Apple's old instantiation prevention pattern.
#implementation AAA
+ (id)alloc
{
return [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
[self doesNotRecognizeSelector:_cmd];
abort();
}
+ (instancetype)theController
{
static AAA* c1 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
c1 = [[super allocWithZone:nil] init];
// For confirm...
NSLog(#"%#", NSStringFromClass([c1 class])); // Prints AAA
NSLog(#"%#", #([c1 class] == self)); // Prints 1
Class real_superclass_obj = class_getSuperclass(self);
NSLog(#"%#", #(real_superclass_obj == self)); // Prints 0
});
return c1;
}
#end
Read this answer and then go and read the other answer.
You must first know what does a Singleton mean and what are its requirements, if you don't understand it, than you won't understand the solution--at all!
To create a Singleton successfully you must be able to do the following 3:
If there was a race condition, then we must not allow multiple instances of your SharedInstance to be created at the same time!
Remember and keep the value among multiple invocations.
Create it only once. By controlling the entry point.
dispatch_once_t helps you to solve a race condition by only allowing its block to be dispatched once.
Static helps you to “remember” its value across any number of
invocations. How does it remember? It doesn't allow any new instance with that exact name of your sharedInstance to be created again it just works with the one that was created originally.
Not using calling alloc init (i.e. we still have alloc init methods since we are an NSObject subclass, though we should NOT use them) on our sharedInstance class, we achieve this by using +(instancetype)sharedInstance, which is bounded to only be initiated once, regardless of multiple attempts from different threads at the same time and remember its value.
Some of the most common system Singletons that come with Cocoa itself are:
[UIApplication sharedApplication]
[NSUserDefaults standardUserDefaults]
[NSFileManager defaultManager]
[NSBundle mainBundle]
[NSOperations mainQueue]
[NSNotificationCenter defaultCenter]
Basically anything that would need to have centralized effect would need to follow some sort of a Singleton design pattern.
Alternatively, Objective-C provides the +(void)initialize method for NSObject and all its sub-classes. It is always called before any methods of the class.
I set a breakpoint in one once in iOS 6 and dispatch_once appeared in the stack frames.
Singleton Class : No one can create more than one object of class in any case or through any way.
+ (instancetype)sharedInstance
{
static ClassName *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ClassName alloc] init];
// Perform other initialisation...
});
return sharedInstance;
}
// You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only.
-(MyClass)init
{
return [ClassName sharedInstance];
}
There are two issues with the accepted answer, which may or may not be relevant for your purpose.
If from the init method, somehow the sharedInstance method is called again (e.g. because other objects are constructed from there which use the singleton) it will cause a stack overflow.
For class hierarchies there is only one singleton (namely: the first class in the hierarchy on which the sharedInstance method was called), instead of one singleton per concrete class in the hierarchy.
The following code takes care of both of these problems:
+ (instancetype)sharedInstance {
static id mutex = nil;
static NSMutableDictionary *instances = nil;
//Initialize the mutex and instances dictionary in a thread safe manner
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mutex = [NSObject new];
instances = [NSMutableDictionary new];
});
id instance = nil;
//Now synchronize on the mutex
//Note: do not synchronize on self, since self may differ depending on which class this method is called on
#synchronized(mutex) {
id <NSCopying> key = (id <NSCopying>)self;
instance = instances[key];
if (instance == nil) {
//Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
id allocatedInstance = [self alloc];
//Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
//Do this right after allocation to avoid the stackoverflow problem
if (allocatedInstance != nil) {
instances[key] = allocatedInstance;
}
instance = [allocatedInstance init];
//Following code may be overly cautious
if (instance != allocatedInstance) {
//Somehow the init method did not return the same instance as the alloc method
if (instance == nil) {
//If init returns nil: immediately remove the instance again
[instances removeObjectForKey:key];
} else {
//Else: put the instance in the dictionary instead of the allocatedInstance
instances[key] = instance;
}
}
}
}
return instance;
}
#import <Foundation/Foundation.h>
#interface SingleTon : NSObject
#property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;
#end
#import "SingleTon.h"
#implementation SingleTon
+(SingleTon *) theSingleTon{
static SingleTon *theSingleTon = nil;
if (!theSingleTon) {
theSingleTon = [[super allocWithZone:nil] init
];
}
return theSingleTon;
}
+(id)allocWithZone:(struct _NSZone *)zone{
return [self theSingleTon];
}
-(id)init{
self = [super init];
if (self) {
// Set Variables
_name = #"Kiran";
}
return self;
}
#end
Hope above code will help it out.
if you need to create singleton in swift,
class var sharedInstance: MyClass {
struct Singleton {
static let instance = MyClass()
}
return Singleton.instance
}
or
struct Singleton {
static let sharedInstance = MyClass()
}
class var sharedInstance: MyClass {
return Singleton.sharedInstance
}
you can use this way
let sharedClass = LibraryAPI.sharedInstance