Using NS_ENUM from Objective-C in Swift - ios

I am using a third party library in my Objective-C project that has an enum defined as:
typedef NS_ENUM(NSUInteger, RJBEvent)
{
RJB_EVENT_OK = 1,
RJB_EVENT_ERROR=2,
RJB_EVENT_START = 4,
};
Then in Objective-C, I can do the following:
[self.rjbLib listenForEvents:(RJB_EVENT_START|RJB_EVENT_OK|RJB_EVENT_ERROR)];
As an exercise to teach myself Swift, I'm porting the app. All's well until I run up against using this enum. There's a ton of info out there about how (or how not to) use enums in Swift, but very little to describe this bitmask-style usage. I've got this, and it compiles, but I'm not receiving the expected event notifications.
let rjbEventsMask : UInt32 = UInt32(RJBEvent.RJB_EVENT_OK.rawValue |
RJBEvent.RJB_EVENT_ERROR.rawValue |
RJBEvent.RJB_EVENT_START.rawValue)
I do see a suggestion on NSHipster that I may need to change the third-party header file to use NS_OPTIONS. I'm going to try that, but changing the developer's provided .h file is a bit dangerous, so it's not my preferred approach.
Any guidance is appreciated.
Thanks!
Rob

Related

Objective-C pointer and swift

I'm following an apple document, but unfortunately the examples are written on objective-c, but I have confidence with Swift language and can not understand the meaning of some things, in particular, in this example:
void RunLoopSourcesPerformRoutine (void *info){
RunLoopSource* obj = (RunLoopSource*)info;
[obj sourceFired];
}
this line: RunLoopSource* obj = (RunLoopSource*)info;
the parameter: void *info indicates that info is a pointer to void, then I can put the address of any type of data structure, following various apple documents I saw that the translation of this : void *info into swift language is :
info: UnsafeMutableRawPointer?
Now, the RunLoopSource* obj = (RunLoopSource*)info; line indicates that obj is a variable of type: RunLoopSource, and to this is assigned the value of (RunLoopSource *) info, but precisely What does it mean this statement? : (RunLoopSource *) info, and how it translates in swift language ?
Swift really hates pointer. These 2 lines of code can be converted to Swift as
func RunLoopSourcesPerformRoutine(info: UnsafeMutableRawPointer) {
let obj = info.assumingMemoryBound(to: RunLoopSource.self)
obj.pointee.sourceFired()
}
This specific expression is a "typecast": it's saying that info, which is declared to be a pointer-to-unknown-anything (void *) is actually known by the programmer to be a pointer to a RunLoopSource. This forcibly changes the type of the expression to make the compiler happy as it is assigned to obj.
It is equivalent to using as! in Swift and is idiomatic when you know the semantics of a void * but the syntax doesn't capture it.
(This attempts to answer your question as stated but I'm not sure if you are looking for more information. If so, please clarify and me or someone more expert in unsafe pointers in Swift can help out.)
What you are dealing with (void *info) is a C pointer-to-void, which arrives into Swift as a form of UnsafeRawPointer. This means that type info has been cast away and that memory is being managed elsewhere.
In order to work with this thing as what you believe it to be, i.e. a RunLoopSource, you need to characterize it explicitly as a RunLoopSource. In C, you would cast, as in the example code you posted: (RunLoopSource*)info. In Swift, you rebind.
Observe that in your case this whole thing has been made just a little more complicated by the fact that this UnsafeMutableRawPointer has been wrapped in an Optional, and will have to be unwrapped before you can do anything at all.
Assuming, then, in your case, that info is really an UnsafeMutableRawPointer? bound to a RunLoopSource, you can say:
let rlsptr = info!.assumingMemoryBound(to: RunLoopSource.self)
let rls = rlsptr.pointee
Now rls is a RunLoopSource and you can work with it however you like. Keep in mind, however, that the memory is unmanaged, so you should work with it only here and now.
EDIT By the way, Apple has a really nice document on this entire matter: https://swift.org/migration-guide/se-0107-migrate.html

Getting "use of undeclared type 'NoError'" with ReactiveCocoa

I am trying to learn ReactiveCocoa and have a hard time getting started. I keep hitting minor bumps as API and tutorials seems to be outdated quickly. Maybe I have the wrong impression.
Just trying to follow this I do not seem to have NoError.
It should be imported correctly, since I have access to Signal, rac_textSignal etc. but I don't know why NoError is not available.
Their documentation mentions NoError as well but that leads to a 404.
This transition to RAC4 mentions NoError as well. Why is NoError undeclared? I am using ReactiveCocoa 4.0.1.
Edit: I just added public enum NoError : ErrorType {} to the top of the file and it works now. I am not sure if this is a proper solution to the problem though. It is not mentioned in guides and tutorials that I should extend ErrorType myself.
The reactive cocoa native NoError was removed in 4.0.1 in favour of antitypicals implementation in Result (adds NoError to Result, see this). See e.g. issue #2704
https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2704
We can see this explicitly used in the source files, e.g.
import enum Result.NoError in Property.swift.
Hence, you probably need to include (antitypicals) Result whenever you intend to use NoError. One suggested fix in the issue thread is
public typealias NoError = Result.NoError
If you are seeing this now with ReactiveSwift 6.0, they removed the dependency on Result, which removes NoError.
Per their release notes here, the solution is to now use Never.
If you have used Result only as dependency of ReactiveSwift, remove
all instances of import Result, import enum Result.NoError or import
struct Result.AnyError and remove the Result Framework from your
project.
Replace all cases where NoError was used in a Signal or
SignalProducer with Never
The following example code shows how this should look now:
import ReactiveSwift
func example() -> SignalProducer<Void, Never> {
return SignalProducer(value: ())
}
If you add "import Results" to the top of the page above your class, NoError will no longer be an undeclared type!

Swift 2 - Visualizing an AVMutableComposition for Debugging : Converting Apple's AVCompositionDebugViewer

I'm running into an issue with my swift 2 conversion of an Apple provided example for displaying an AVMutableComposition. This is a really useful project if you're trying to visualize your AVComposition to see what might be going on under the hood.
Update: I added print statements to each function to see the order they are being called, and who is calling them, in comparison to the Obj-C project.
Two Issues that I'm seeing that seem pretty important:
synchronizePlayerWithEditor() is not getting called after buildTransitionComposition(_:andVideoComposition:andAudioMix:)
observeValueForKeyPath(_:...) is NOT being called in the same order as the Obj-C project
Posting the snippet here to get the calling function as it's kind of useful
Obj-C
NSLog(#"%d %s %#", __LINE__, __PRETTY_FUNCTION__, [[NSThread callStackSymbols] objectAtIndex:1]);
Swift
func functionnYouWantToPrintCaller(yourNormalParameters..., function:String = __FUNCTION__){...}
print("\(__LINE__) \(__FUNCTION__) \(function)
Here is Apple's AVCompositionDebugViewer project I'm working from: https://developer.apple.com/library/mac/samplecode/AVCompositionDebugViewer
My github repo:
https://github.com/justinlevi/iOSAVCompositionDebugViewerSwift
I think the issue might be stemming from something in the keyValueObservation code although I'm not entirely sure at this point.
The issue ended up being in SimpleEditor.swiftbuildCompositionObjectsForPlayback` method. There were some global variables that were being defined incorrectly.
Everything seems to be working as expected now.
https://github.com/justinlevi/iOSAVCompositionDebugViewerSwift

Getting "Expected a type" error in XCode

I'm getting this error:
/Class/GData/OAuth/GDataOAuthViewControllerTouch.m:116:22: Expected a type
That line is:
authentication:(GDataOAuthAuthentication *)auth
Inside of this block of code:
- (id)initWithScope:(NSString *)scope
language:(NSString *)language
requestTokenURL:(NSURL *)requestURL
authorizeTokenURL:(NSURL *)authorizeURL
accessTokenURL:(NSURL *)accessURL
authentication:(GDataOAuthAuthentication *)auth
appServiceName:(NSString *)keychainAppServiceName
delegate:(id)delegate
finishedSelector:(SEL)finishedSelector {
NSString *nibName = [[self class] authNibName];
I'm a newb XCode developer. So far I've created and compiled a calculator app based from an online class but that's it.
Is this a library that is not being included?
Background: The previous developer abandoned the project and the owner sent the project code to me. I'm trying to replace the existing graphics with new graphics and recompile it with support for iOS 6, which I thought I should be able to do without any coding, but have run into this error and many others when I opened the project. I have the latest XCode.
The :22 (and the position of the caret within the editor) tell you exactly where on the line the error is. In this case it's telling you that where it sees GDataOAuthAuthentication it was expecting a type. So, implicitly, it doesn't recognise that GDataOAuthAuthentication is a type.
Objective-C still sits upon compilation units ala C — each .m file is compiled in isolation then the lot are linked together. You use #import (or #include if you want; #import just guarantees the same file won't be included twice) to give each individual file visible sight of any external definitions it needs.
So, that's a long-winded way of reaching the same conclusion as Rick did five minutes ago: you've probably omitted a necessary #import.
A few things to look for:
Did you #import the file where the GDataOAuthAuthentication type is defined? (e.g. #import "GDataOAuthAuthentication.h")
Is there a variable named GDataOAuthAuthentication which is causing the compiler to think GDataOAuthAuthentication is a variable not a type?

ios : NSArray of CFUUIDRef

I'm trying to use CoreBluetooth's retrievePeripheral :
- (void)retrievePeripherals:(NSArray *)peripheralUUIDs;
The documentation says peripheralUUIDs should be a NSArray of CFUUIDRef. In the Apple sample project temperatureSensor, it is called as :
[centralManager retrievePeripherals:[NSArray arrayWithObject:(id)uuid]];
(uuid being a CFUUIDRef)
When I use the exact same code in XCode 4.5.1, IOS6, I'm getting a error :
Cast of C pointer type 'CFUUIDRef' (aka 'const struct __CFUUID *') to Objective-C pointer type 'id' requires a bridged cast
I would say (though I'm far from sure) that the reason it works in TemperatureSensor and not in my project is because TemperatureSensor seems not to use ARC whereas my project does.
Xcode suggests 2 ways of solving the problem : adding a __bridge or using CFBridgingRelease(). I tried them both and I'm under the impression that the function does not work [Edit] because the delegate methode didRetrievePeripheral: never gets called [/Edit] (my understanding is that these operation would change the C-style structs into objective-C-objects thus creating a NSUUID, and the method can't use it, but, again I'm really not sure)
So what should I do ? I've been searching on google for examples of retrievePeripherals using ARC, but without success.
In the temperature sensor change this line and run
LeDiscovery.m
-(void) startScanningForUUIDString:(NSString *)uuidString
{
[centralManager scanForPeripheralsWithServices:nil options:0];
}
change the word nil and assume 0.
If you want more check this link.
I hope its useful for you.
Turns out the problem was much simpler than that. I copied/pasted some code from TemperatureSensor, specifically the DidRetrievePeripheral. But it turns out, there's an error in this code (it's DidRetrievePeripheralS), so the delegate method never gets called. I think the bug is already reported.
Thanks/sorry

Resources