Should an IBOutlet property be marked nullable or nonnull? - ios

In Objective-C I'm used to declaring properties that connect to Interface Builder with
#property (nonatomic, weak) IBOutlet UIView *myView;
Now I have a class that uses the new XCode nullability attributes. To preserve compatibility with Swift, what nullability attribute should an IBOutlet have? According to Apple's "Using Swift with Cocoa and Objective-C":
When you declare an outlet in Swift, you should make the type of the outlet an implicitly unwrapped optional. This way, you can let the storyboard connect the outlets at runtime, after initialization. When your class is initialized from a storyboard or xib file, you can assume that the outlet has been connected.
So does this mean the outlet should be declared nonnull in Objective-C?

If your class is written in Swift, you can't use a non optional property because otherwise the compiler is going to complain that the property is never initialized. That's why Apple recommends to declare it as an implicitly unwrapped optional, because once your object has been initialized, you know for sure that the property contains a value (unless you have a dangling outlet, which can happen by the way...)
When exporting from Objective-C, you can mark it as nonnull and it will appear in Swift as a non optional property which is fine in that case. Note that you can't use both nonnull and weak.
So you can either do :
#property (nonatomic, strong, nonnull) IBOutlet UIView *subview;
// Exported to Swift as #IBOutlet var subview: UIView
or
#property (nonatomic, weak, nullable) IBOutlet UIView *subview;
// Exported to Swift as #IBOutlet weak var subview: UIView?
If for some reason you still want the property to be exported to Swift as an implicitly unwrapped optional, you can mark the property as null_resettable or null_unspecified. That's not really what they are meant for but it will still produce the desired result. See this blog post for more on these annotations.
#property (nonatomic, weak, null_unspecified) IBOutlet UIView *subview;
// Exported to Swift as #IBOutlet weak var subview: UIView!

The declaration of weak implies that the property is nullable. Therefore this works
#property (nonatomic, weak, nullable) IBOutlet UIView *contentView;
Declaring the property nonnull gives an error.

Related

Xcode Property Not Retaining Value

I have a view controller called LoginWindowViewController.h that declared a property called usernameTextField:
#property (strong, nonatomic) IBOutlet UITextField *usernameTextField;
A string called James is associated to this TextField.
I then import another view controller,FirstViewController.h into my LoginWindowViewController.m and I also imported LoginWindowViewController.h into FirstViewController.m . In my FirstViewController.h there is a property called username.
#property (strong, nonatomic) IBOutlet NSString *userName;
Then i assign usernameTextField to username(In my FirstViewController.m). But when i NSlog the property username in my FirstViewController.m, it gives a null value.How do i fix this?
When you import classes you do not actually import any values. When you set the value of a property it is only set on that instance of the class. You will need to explicitly reference the property of your current instance to get the value you have set.
One note: IBOutlet stands for Interface Builder Outlet and is how you create a link from a storyboard or xib file UI element to a class property. So, no need to use IBOutlet if you are not linking to something in interface builder.
String should be declared
#property (nonatomic, strong) NSString *userName;
Then when you instantiate your login view controller from first view controller you can set the property like this
LoginWindowViewController *loginVC = [[LoginWindowViewController alloc] init];
[loginVC.usernameTextField setText:self.userName];

The use of #property

Why is this code okay?
#interface AddViewController : UIViewController <UIPickerViewDelegate>
{
IBOutlet UILabel *category;
IBOutlet UIPickerView *categoryPicker;
}
#end
But this code is not
#interface AddViewController : UIViewController <UIPickerViewDelegate>
IBOutlet UILabel *category;
IBOutlet UIPickerView *categoryPicker;
#end
Also, I was looking up a video on how to use the UIPickerView, and instead of making properties for the IBOutlet, he just made the outlets. Which clearly has something to do with why those curly braces are there.
category and categoryPicker are instance variables, not properties. You either reference them directly via their name e.g. category or by using self->category.
Instance variables are declared in the curly braces, while properties are declared with #property
Instance variables do not have setter and getter methods, while properties have.
you need to change "IBOutlet UILabel *category;" to "#property (nonatomic, strong) IBOutlet UILabel *category;", the same for "IBOutlet UIPickerView *categoryPicker;"
Within the bracket {}, category and categoryPicker are instance variables, but if you delete the "{}", you need to use "property" so that they work the same way

iOS: __weak vs (weak)

Are there a differences between these two lines of code?
__weak IBOutlet UITextField *usernameField;
#property (weak) IBOutlet UITextField *usernameField;
What if you declare either of these in interface section of the .h or the .m files?
Yes. The first example declares a weak instance variable called usernameField, but the second declares a weak property called usernameField, and an instance variable called _usernameField that is accessed by the property.
If you declare it in an #interface section of the .m file, then it can only be accessed in that .m file (unless you mess with the Objective-C runtime).
The difference is not in the weak reference but just in the fact that the first is an instance and the second is a #property.
__weak and (weak) is the same thing, but the second is used as attribute for properties.

Should I make my ivars properties in iOS?

Just when you think you understand something, you don't! :)
I understand that if I make a variable a property, I can access it anywhere in the Class and even set it from outside that class.
I thought if I didnt need it I could just make it an ivar. So I have a viewcontroller with about 5 UILabels. So in its viewDidLoad I say:
pharmacyName.text = self.receivedLocation.name;
pharmacyTel1.text = #"556-7843";
pharmacyTel2.text = #"991-2345";
pharmacyTel3.text = #"800-0001";
When I have declared them like so in the .h file:
#interface DetailViewController : UIViewController{
IBOutlet UILabel *pharmacyName;
IBOutlet UILabel *pharmacyTel1;
IBOutlet UILabel *pharmacyTel2;
IBOutlet UILabel *pharmacyTel3;
}
#property (nonatomic,strong) MyLocation *receivedLocation;
#end
No. Its not mandatory to create ivar as property. If you don't want to access it outside of class just use as it is. In ARC you can also declare your IBOutlet as below:
#interface DetailViewController : UIViewController{
__weak IBOutlet UILabel *pharmacyName;
__weak IBOutlet UILabel *pharmacyTel1;
__weak IBOutlet UILabel *pharmacyTel2;
__weak IBOutlet UILabel *pharmacyTel3;
}
This will keep a week reference of outlets. Here is detail of __weak and strong
There are always many ways you can approach programming tasks and standards. Our group has started using a few coding standards. We like to put our instance variables that are NOT accessed from outside the class (and protocol statements) in the private interface in the .m file like this:
#interface DetailViewController() {
NSString *value_;
}
#end
We also like to use #property for our instance ivars and declare those in the private interface as well like this:
#interface DetailViewController() {
}
#property (nonatomic, strong) IBOutlet UIlabel *pharmacyName;
#end
and then in your code, you would refer to this as self.pharmacyName. It seems to work pretty well with autocomplete, and with getting and setting. Also when you have thread safety issues, the nonatomic, strong behavior comes in handy.

addSubview for Storyboard created views

At least I think it's a basic problem. I just started working with views programmatically.
In RouteCaptureViewController.h:
#property (strong, nonatomic) IBOutlet UIView *routeCaptureSuperView;
#property(nonatomic, weak) IBOutlet UIImageView *captureImageView;
#property(nonatomic, retain) IBOutlet UIImageView *previewImageView;
#property (weak, nonatomic) IBOutlet UIView *captureRouteButtonView;
In my storyboard:
All of the outlets are properly connected, I checked.
I'm implementing addSubview in a method as such and nothing happens:
[self.routeCaptureSuperView addSubview:self.captureRouteButtonView];
[self.routeCaptureSuperView addSubview:self.captureImageView];
The following lines worked previously in the code:
[self.captureImageView removeFromSuperview];
[self.captureRouteButtonView removeFromSuperview];
And I know self.routeCaptureSuperView is not nil from an NSLog.
If I understood you correctly and you removed the views to add them again later I can make an educated guess:
In the moment you send removeFromSuperview to your views they get deallocated because they are declared as weak only.
Weak means that the property will be nil'd if the object is deallocated because the last strong relationship to that object is released.
The parent view is the object that keeps the last strong relationship to those two views.
Try to change weak to strong in the #property declaration of the two subviews.

Resources