Having trouble converting a simple Objective-C block to Swift - objective-c

Not sure what I'm doing wrong. I'm using this module https://github.com/wenzhaot/InstagramPhotoPicker (the header file for the block is here https://github.com/wenzhaot/InstagramPhotoPicker/blob/master/TWPhotoPicker/TWPhotoPickerController.h)
In Objective-C, the syntax is:
photoPicker.cropBlock = ^(UIImage *image) {
//do something
};
I tried converting it to swift, but I'm getting an error
photoPicker.cropBlock{ (image:UIImage) -> () in
//Do something
//error: cannot invoke 'cropBlock' with an argument list of type '((UIImage) -> ())'
}
How to get it to work?

You're just missing = sign. Also image is implicitly unwrapped optional:
photoPicker.cropBlock = { (image: UIImage!) -> () in
//Do something
}
or
photoPicker.cropBlock = { image in
//Do something
}

Related

C API with structures in Swift - immutable value as inout argument

I'm working on a Swift 3 project that involves using some C APIs that I bridged from Objective-C.
Here is a sample snippet of the structure of the API:
typedef struct
{
StructMode mode;
StructLevel level;
} TargetStruct;
typedef struct
{
. . .
TargetStruct *targetStruct;
OtherStruct *otherStruct;
NonPointerStructA nonPointerStructA;
NonPointerStructB nonPointerStructB;
. . .
} InnerStruct;
typedef struct
{
InnerStruct innerStruct;
OtherStructB otherStructB;
} OuterStruct;
In my Swift code, my goal is to set a value of the TargetStruct from the OuterStruct, like the following:
// run function that returns an instance of TargetStruct
var targetStruct: TargetStruct = initializeTargetStruct()
// assign targetStruct to outerStruct
outerStruct.innerStruct.targetStruct = &targetStruct
However, I am getting the following error:
Cannot pass immutable value of TargetStruct as inout argument
If I set a value of a struct without the *, it will work fine:
var nonPointerStructA: NonPointerStructA = initializeNonPointerStructA()
outerStruct.innerStruct.nonPointerStructA = nonPointerStructA
I have tried setting the value of targetStruct like this, but for now I have no way to test it:
var targetStruct: TargetStruct = initializeTargetStruct()
outerStruct.innerStruct.targetStruct.initialize(from: &targetStruct, count: 0)
How to solve this problem? Thank you.
In Swift, prefix & is not an address-of operator. It is just needed to clarify that some expression is passed to an inout parameter. So, your first code is syntactically invalid in Swift.
Your C-structs are imported to Swift as follows:
struct TargetStruct {
var mode: StructMode
var level: StructLevel
//some auto generated initializers...
}
struct InnerStruct {
//...
var targetStruct: UnsafeMutablePointer<TargetStruct>!
var otherStruct: UnsafeMutablePointer<OtherStruct>!
var nonPointerStructA: NonPointerStructA
var nonPointerStructB: NonPointerStructB
//some auto generated initializers...
}
struct OuterStruct {
var innerStruct: InnerStruct
var otherStructB: OtherStructB
//some auto generated initializers...
}
(If something wrong, please tell me.)
As you see, targetStruct in your InnerStruct is a pointer, and initialize(from:count:) tries to write to the pointed region, but at the time you call initialize(from:count:), targetStruct holds its initial value nil, you know what happens when dereferencing null-pointer.
One way is to allocate a memory for the TargetStruct and use the pointer to the allocated region.
func allocateAndInitializeTargetStruct() -> UnsafeMutablePointer<TargetStruct> {
let targetStructRef = UnsafeMutablePointer<TargetStruct>.allocate(capacity: 1)
targetStructRef.initialize(to: initializeTargetStruct())
return targetStructRef
}
outerStruct.innerStruct.targetStruct = allocateAndInitializeTargetStruct()
This is a more general way than below, but you need to explicitly deinitialize and deallocate the allocated region. That's sort of hard to manage.
If you can confine the usage of the outerStruct in a single code-block, you can write something like this:
var targetStruct = initializeTargetStruct()
withUnsafeMutablePointer(to: &targetStruct) {targetStructPtr in
outerStruct.innerStruct.targetStruct = targetStructPtr
//Use `outerStruct` only inside this code-block
//...
}
In this case, the pointer held in outerStruct.innerStruct.targetStruct (== targetStructPtr) is only valid inside the closure and you cannot use it outside of it.
If any of the codes above does not fit for your use case, you may need to provide more context to find the best solution.
An example of nested use of withUnsafeMutablePointer(to:_:):
var targetStruct = initializeTargetStruct()
var otherStruct = initializeOtherStruct()
withUnsafeMutablePointer(to: &targetStruct) {targetStructPtr in
withUnsafeMutablePointer(to: &otherStruct) {otherStructPtr in
outerStruct.innerStruct.targetStruct = targetStructPtr
outerStruct.innerStruct.otherStruct = otherStructPtr
//Use `outerStruct` only inside this code-block
//...
}
}
When you need more pointers to set, this nesting would be a mess, but it's the current limitation of Swift.
An example of deinitialize and deallocate:
extension InnerStruct {
func freeMemberStructs() {
if let targetStructRef = targetStruct {
targetStructRef.deinitialize()
targetStructRef.deallocate(capacity: 1)
targetStruct = nil
}
if let otherStructRef = otherStruct {
otherStructRef.deinitialize()
otherStructRef.deallocate(capacity: 1)
otherStruct = nil
}
}
}
outerStruct.innerStruct.targetStruct = allocateAndInitializeTargetStruct()
outerStruct.innerStruct.otherStruct = allocateAndInitializeOtherStruct()
// Use `outerStruct`
//...
outerStruct.innerStruct.freeMemberStructs()
The code may not seem to be too complex (just a bunch of boilerplate codes), but it's hard to find when or where to do it. As your InnerStruct may be embedded in another struct which may need to be deinitilized and deallocated...
Hope you can find your best solution.

Objective C Array completionHandler used in Swift

I have a completion handler in a framework written in Objective C...
This is a typedef for a block type. It takes an array of PHErrors.
typedef void (^PHBridgeSendErrorArrayCompletionHandler)(NSArray *errors);
When I try to use this in Swift, I'm doing....
anObject.aMethod(completionHandler: { (errors: [ AnyObject?]) -> () in
...rest of code
}
But I keep getting this error:
Cannot convert value of type '([AnyObject?]) -> ()' to expected argument type 'PHBridgeSendErrorArrayCompletionHandler!'
Can anyone help, I'm baffled, it looks like it should work to me.
Or better yet, you can still use your typedef as typealias.
DEFINE
typealias PHBridgeSendErrorArrayCompletionHandler = (_ errors: [Error]?) -> Void
IMPLEMENTATION
func myFunctionWithErrorCompletion(completion: PHBridgeSendErrorArrayCompletionHandler) {
// Define empty array to add errors to
var errors:[Error]?
// Do Your Logic that may store errors to array
// Completion and pass errors
completion(errors)
}
USAGE
func anotherOfMyFunctions() {
// Call the function
myFunctionWithErrorCompletion { (errors) in
if let completionErrors = errors {
// React to errors
}
}
}
anObject.aMethod(completionHandler: { (errors: [ AnyObject?]) -> () in
}
should be
anObject.aMethod() { errors in
}
In order to dig any deeper, I have to know what PHBridgeSendErrorArrayCompletionHandler is
So my friend solved this problem by simply changing AnyObject to Any
(errors: [Any]?) in
Which baffles me because all objects in an NSArray are objects! So didn't think to try Any.
Im pretty new to Swift mind
Try this..
anObject.aMethod(completionHandler: { (errors:NSArray?) -> () in
...rest of code
}

what is the proper syntax for the following expression in swift 3?

as you can guess, this is an issue regarding swift's whole API renovation. I've read the documentation extensively but can't seem to lay my finger on a proper workaround.
I am receiving the error
Value of type 'Error' has no member 'userInfo'
on the following line:
else if let secondMessage = error?.userInfo["error"] as? String
of this block:
let query = PFQuery(className: "Images")
query.whereKey("Subject", equalTo: self.subjectName)
query.findObjectsInBackground { (objects, error) -> Void in
if error == nil {
// do something
}else if let secondMessage = error?.userInfo["error"] as? String {
// do something
}
Any suggestions? feel's like I'm missing something painstakingly obvious... dev block i guess :(

Missing argument for parameter #1 in call

I am doing a Obj-C to Swift Conversion, and am hitting a roadblock. I am getting the error 'Missing argument for parameter #1 in call' after calling
self.tabataTimerUpdated() //error
This function that is being called (which I converted to swift) is:
func tabataTimerUpdated(NSNotification) {
self.showTime()
}
Here is what it looks like in it's original Obj-C form:
- (void)tabataTimerUpdated:(NSNotification *)notification {
[self showTime];
}
Where did I go wrong in the conversion, and how can I fix it? Thanks.
You get the error because you have
func tabataTimerUpdated(NSNotification) {
}
try remove NSNotification, look like this:
func tabataTimerUpdated() {
}
or specify something in there, like this:
func tabataTimerUpdated(notification: NSNotification){
}
and when you call the function, use:
self.tabataTimerUpdated(yourNotificationVariableInHere)

iap - conversion from objective c to swift

I'm trying to convert my app from objective c to swift, and i have everything except for the in app purchase working.
i have the objective c helper class imported in the project, but i'm having trouble doing the RequestProductsCompletionHandler section
in the old objective c version i have the code
[[MTIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
if([[self appData] isPro] == FALSE)
[[self bUpgrade] setUserInteractionEnabled:TRUE];
}
}];
and i'm trying to convert this section to objective c but so far i've been unable to convert the requestProductsWithCompletionHandler part
can anyone help?
i've tried creating the completion handler the same was as in objective c using
requestProductsWithCompletionHandler(sucess: Bool, products : NSArray)
but i get the compiler errors "Extra argument 'products' in call" and "Expected member name or constructor call after type name".
if i try it without the parameters i get "Missing argument for parameter #1 in call"
In Swift, your completion handler would be a closure. The syntax is semi-similar to blocks in Objective-C but, instead of defining the parameters outside the block:
^(BOOL success, NSArray *products) { /* ... */ }
you define them inside the closure:
{ (success: Bool, products: [AnyObject]!) in /* ... */ }
So, your call to requestProductsWithCompletionHandler should look something like this:
MTIAPHelper.sharedInstance().requestProductsWithCompletionHandler {
(success: Bool, products: [AnyObject]!) in
if success {
// etc.
}
}
You can also let Swift infer the parameter types for you:
MTIAPHelper.sharedInstance().requestProductsWithCompletionHandler {
(success, products) in
if success {
// etc.
}
}