How can method in Swift with inout parameter be used in Objective-C? - objective-c

I want
func foo(inout stop: Bool) -> Void {
// ...
}
use in my Objective-C part. But it is never generated in Module-Swift.h header. If I mark it with #objc, the
Method cannot be marked #objc because the type of the parameter cannot
be represented in Objective-C
error occurs.

You can't use an inout parameter when bridging with Objective-C, but you can do something similar if you use an UnsafeMutablePointer<T> (T would be Bool in your case). It would look something like this:
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
if stop != nil {
// Use the .pointee property to get or set the actual value stop points to
stop.pointee = true
}
}
Example
TestClass.swift:
public class TestClass: NSObject {
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
stop.pointee = true
}
}
Objective-C:
TestClass *test = [[TestClass alloc] init];
BOOL stop = false;
[test foo:&stop];
// stop is YES here

Similarly to what happening with generics, inout is not objc-compatible.
One possible workaround is to embed your parameter(s) in a class (which is a reference type, hence passed by pointer and not by value):
#objc class MyFuncParams {
var stop: Bool
init(stop: Bool) {
self.stop = stop
}
}
and define the function to accept an instance of that class:
func changeParam(params: MyFuncParams) {
params.stop = true
}
Not an elegant way to solve the problem, but what's important is that it should work (never tried myself though).

Related

How to use Swift type nested in extension in objective-c?

I essentially want an enum of UIColors broken by which team would use them. The hope is that it could be accessed easily. Something like:
UIColor.Residential.white or UIColor.Commercial.white
The difficulty has been finding something that works in both Swift and Objective-C. Trying all manner of #objc and #objMembers in front of the extension and class declarations has stumped me. Is there a pattern for this kind of thing?
Something like the below would be ideal.
extension UIColor {
#objc class Residential {
static let navbarBlue = UIColor.color(hexString: "003865")
}
#objc class Commercial {
static let navbarBlue = UIColor.color(hexString: "010101")
}
}
Right now, I'm using the below, and it's working fine, but I dislike the naming duplication.
extension UIColor {
#objc static let residentialDarkBlueNavBar = UIColor.color(hexString: "003865")
#objc static let commercialLightBlueNavBar = UIColor.color(hexString: "0B60BB")
#objc static let residentialBackgroundNavBar = UIColor.color(hexString: "0E49AD")
#objc static let commercialBackgroundGray = UIColor.color(hexString: "F2F2F6")
}
Edit
The other question I've been referenced to recommends calling class methods to calculate the colors from a hex. But these are static variables that do not need to be calculated when called. Yes, the performance hit is negligible, but I'd like to access organized static variables in both Swift and ObjC without calling a method each time. If that's not possible, I'll do what the other question says. I just want to know if it is.
Thanks!
You can't use Swift enums in Objective-C unless they declare an integer raw type so to adhere to your specifications and be available between both languages it becomes convoluted to achieve what you're asking. You will have to create an enum for each set of colors containing the RGB Int value which is then used in a class function for each color type that only accepts colors within it's associated enum. Those functions then call a function to unpack the RGB value from the int which is then used to create and return the UIColor.
The requirement to keep the colors as a hex string and use enums between both languages isn't currently possible due interoperability restrictions of enums between Swift and Obj-C.
#objc public enum Residential: NSInteger {
case darkBlue = 14437
}
#objc public enum Commercial: NSInteger {
case lightBlue = 745659
}
extension UIColor {
#objc static func commercial(_ rgb: Commercial) -> UIColor {
return UIColor.rgb(rgb.rawValue)
}
#objc static func residential(_ rgb: Residential) -> UIColor {
return UIColor.rgb(rgb.rawValue)
}
#objc class func rgb(_ rgb: Int) -> UIColor {
let iBlue = rgb & 0xFF
let iGreen = (rgb >> 8) & 0xFF
let iRed = (rgb >> 16) & 0xFF
let iAlpha = (rgb >> 24) & 0xFF
return UIColor.init(red: CGFloat(iRed)/255, green: CGFloat(iGreen)/255,
blue: CGFloat(iBlue)/255, alpha: CGFloat(iAlpha)/255)
}
}
Useage:
let residentialBlue = UIColor.residential(.darkBlue) // Swift useage
UIColor *residentialBlue = [UIColor residential:ResidentialBlue]; // Obj-C useage
Edit:
The Int to RGB code used to create the UIColor was found and modified from this SO answer.
So the following works:
extension UIColor {
#objc(UIColorResidential) public class Residential: NSObject {
#objc static let navbarBlue = UIColor.color(hexString: "003865")
}
#objc(UIColorCommercial) public class Commercial: NSObject {
#objc static let navbarBlue = UIColor.color(hexString: "010101")
}
}
Don't forget to properly import them: #class UIColorResidential, UIColorCommercial; in .h and #import "YourModule-Swift.h" in .m

Swift call Objective-C wrapper function containing blocks

I have an Objective-C wrapper (ObjCWrapper.h and ObjCWrapper.m) with prototype
+ (void) login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock;
With typedef
typedef void (^SuccessBlock)(NSString *);
and implementation
+ (void)login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock
{
// do stuff like
successBlock(#"test");
}
From my swift view controller (ViewController.swift), I call the login function:
ObjCWrapper.login("abc", andPassword: "abc",
andErrorBlock:
{
(error:String) -> Void in
println();
},
andSuccessBlock:
{
(map:String) -> Void in
println();
}
)
But I get error:
Cannot invoke 'login' with an argument list of type '(String, andPassword: String, andErrorBlock:(String)->void, andSuccessBlock:(String)->void)'
Searching in google says that I am passing some invalid types in the arguments, but I can't find anything wrong in the code. Removing the blocks from the function makes the code work, so I guess it is something related on the way of calling a block function.
Thanks for the help!
It might be worth adding nullability specifiers to your completion block:
typedef void (^SuccessBlock)( NSString * _Nonnull );
And to the method itself:
+ (void) login:(nonnull NSString *)username andPassword:(nonnull NSString *)password andErrorBlock:(nullable SuccessBlock)errorBlock andSuccessBlock:(nullable SuccessBlock)successBlock;
Then you should be able to call your method in Swift:
ObjCWrapper.login("login", andPassword: "pass", andErrorBlock: { (error:String) -> Void in
//error handling
}) { (map:String) -> Void in
//other stuff
}
Obj-C NSString != Swift String
With other word, you pass a String where a NSString is expected.
Casting it down should solve that.
This is what I ended up doing
let username = "username" //usernameField.text
let password = "password" //passwordField.text
ObjCWrapper.login(username, andPassword: password,
andErrorBlock:
{
(map) -> Void in
// stuff
})
{
(map) -> Void in
// stuff
}

NSTextAlignment can not be represented in Objective-C

In my Swift class, I exposed an variable to objective c by defining it as
#objc var textAlignment: NSTextAlignment? {
didSet {
if textAlignment != nil {
label.textAlignment = textAlignment!
}
}
}
And it is Wrong! because compiler complains that it is not a type that can be represented in Objective-C.
But the followings are right
var textAlignment: NSTextAlignment? {
didSet {
label.textAlignment = textAlignment!
}
}
Or
#objc var textAlignment: NSTextAlignment {
didSet {
label.textAlignment = textAlignment
}
}
So it looks like if I expose the method to objective-C, It can not be optional value. However if I use it internally, I can! Is it because it is a scalar value where in Objective-C we have no way to represent it as nil?
My intention was just want to make it not compulsory.
NSTextAlignment is an enum, not an class - the textAlignment variable can't be mapped as optional to objective c because that would require it to be a pointer to an object type.
Try:
#objc var textAlignment: NSTextAlignment {
...
}
Or, if you really need it to be optional, you could expose it to Objective C as an NSNumber, wrapping the textAlignment.rawValue or setting to nil when no value is needed.

OCMock mock protocol's static class method.

New in OCMock 3 is the ability to mock out class methods.
Is it possible to mock class methods defined in a protocol? i.e
#protocol AViewControllerProtocol <NSObject>
+ (Type)typeForViewController;
#end
Inside my unit test class
- (void)testProtocolClassMethod {
id mockedViewController = OCMProtocolMock(#protocol(AViewControllerProtocol));
//This line compiles fine, but throws an exception at run time.
OCMStub([mockedViewController typeForViewController]).andReturn(SomeType);
}
Exception throw
NSInvalidArgumentException: cannot stub/expect/verify method 'typeForViewController' because no such method exists in the mocked class
This looks like it was an oversight in OCMock 3.1, but you can make the fix yourself, if you want.
// OCProtocolMockObject.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, YES);
if(methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, YES);
}
// Add this case for required class methods
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, NO);
}
// Add this case for optional class methods
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, NO);
}
if(methodDescription.name == NULL)
{
return nil;
}
return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
}
I verified this fix with this test:
- (void)testProtocolClassMethod {
id mockedViewController = OCMProtocolMock(#protocol(AViewControllerProtocol));
// FIXED: This line compiles fine, but throws an exception at run time.
OCMStub([mockedViewController typeForViewController]).andReturn(SomeType);
Type type = [mockedViewController typeForViewController];
XCTAssertEqual(type, SomeType, #"Not equal!");
OCMVerify([mockedViewController typeForViewController]);
}
I'll put a request in on the project page for this.

How to write init method in Swift?

I want to write an init method in Swift. Here I initialize an NSObject class in Objective-C:
-(id)initWithNewsDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
self.title = dictionary[#"title"];
self.shortDescription = dictionary[#"description"];
self.newsDescription = dictionary[#"content:encoded"];
self.link = dictionary[#"link"];
self.pubDate = [self getDate:dictionary[#"pubDate"]];
}
return self;
}
How can I write this method in Swift ?
that could be good bases for your class, I guess:
class MyClass {
// you may need to set the proper types in accordance with your dictionarty's content
var title: String?
var shortDescription: String?
var newsDescription: String?
var link: NSURL?
var pubDate: NSDate?
//
init () {
// uncomment this line if your class has been inherited from any other class
//super.init()
}
//
convenience init(_ dictionary: Dictionary<String, AnyObject>) {
self.init()
title = dictionary["title"] as? NSString
shortDescription = dictionary["shortDescription"] as? NSString
newsDescription = dictionary["newsDescription"] as? NSString
link = dictionary["link"] as? NSURL
pubDate = self.getDate(dictionary["pubDate"])
}
//
func getDate(object: AnyObject?) -> NSDate? {
// parse the object as a date here and replace the next line for your wish...
return object as? NSDate
}
}
advanced-mode
I would like to avoid to copy-pand-paste the keys in a project, so I'd put the possible keys into e.g. an enum like this:
enum MyKeys : Int {
case KeyTitle, KeyShortDescription, KeyNewsDescription, KeyLink, KeyPubDate
func toKey() -> String! {
switch self {
case .KeyLink:
return "title"
case .KeyNewsDescription:
return "newsDescription"
case .KeyPubDate:
return "pubDate"
case .KeyShortDescription:
return "shortDescription"
case .KeyTitle:
return "title"
default:
return ""
}
}
}
and you can improve your convenience init(...) method like e.g. this, and in the future you can avoid any possible mistyping of the keys in your code:
convenience init(_ dictionary: Dictionary<String, AnyObject>) {
self.init()
title = dictionary[MyKeys.KeyTitle.toKey()] as? NSString
shortDescription = dictionary[MyKeys.KeyShortDescription.toKey()] as? NSString
newsDescription = dictionary[MyKeys.KeyNewsDescription.toKey()] as? NSString
link = dictionary[MyKeys.KeyLink.toKey()] as? NSURL
pubDate = self.getDate(dictionary[MyKeys.KeyPubDate.toKey()])
}
NOTE: that is just a raw idea of how you could do it, it is not necessary to use conveniece initializer at all, but it looked obvious choice regarding I don't know anything about your final class – you have shared one method only.
class myClass {
var text: String
var response: String?
init(text: String) {
self.text = text
}
}
See Swift: Initialization
Do not need for call this method from other class it will get called automatically
override init()
{
super.init()
// synthesize.delegate = self
// println("my array elements are \(readingData)")
}
try:
initWithDictionary(dictionary : NSDictionary) {
init()
self.title = "... etc"
}
Source:
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html