I'm reading many articles about how you shouldn't check an object for nil. It's a objC paradigm and it's a bad design and w/ swift it's been eliminated. So my question is, per example below, can you pass thru "group" as nil value? does the nil-checking mechanism happen when the function is called, hence removing the need to implement if(group==nil){..} ?
func deleteMembershipForGroup(group:GroupData){
}
You need to use an optional:
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group {
// not nil
}
}
Yes! Thomas Kilian is right and it works for me! You will then be able to pass a nil parameter. You will also notice that using optional variable, it will also removed the warning saying the variable "group" will always be true.
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group { <--- Warning gone!
// not nil
}
}
Related
I have a watch app with complications. Updating the complication on a watch face did work for a long time, but stopped recently, maybe due to a watchOS update.
The reason is that the activeComplications property of the CLKComplicationServer.sharedInstance() is nil, although my complication placeholder is shown on the watch face (device & simulator).
The code could not be simpler:
final class ComplicationController: NSObject, CLKComplicationDataSource {
// …
func updateComplications() {
//…
let complicationServer = CLKComplicationServer.sharedInstance()
if let activeComplications = complicationServer.activeComplications {
for complication in activeComplications {
complicationServer.reloadTimeline(for: complication)
}
}
//…
}
//…
}
If I stop at a breakpoint at the if let instruction, complicationServer has the following values:
And the following lldb command outputs nil:
What could be the reason?
My bad: I solved the problem 4 years ago, but forgot the solution during refactoring of the app.
Actually I don’t know if this is a solution, a workaround or a hack:
I suspect that the CLKComplicationServer or its CLKComplicationDataSource, i.e. the ComplicationController, is not correctly initialized if ComplicationController.shared is executed anywhere in the code. If not, the ComplicationController is correctly initialized by the CLKComplicationServer.
Therefore, one cannot call any function in the ComplicationController, e.g. to update complications. Instead one can send a notification to the ComplicationController that executes the requested function. Of course, one has to ensure that the ComplicationController is already initialized and registered to receive such a notification before it is posted.
If so, CLKComplicationServer.sharedInstance().activeComplications is no longer nil, and the complication update works.
My main issue revolves around dispatch_once. I am converting this objective-c code in Swift :
dispatch_once(&_startupPred, ^{
[MPPush executeUnsafeStartupWithConfig:[MPConfig configWithAppKey:appKey withAppId:appID withAccountId:accountId forProduction:inProduction] authToken:authToken];
});
Swiftify doesn't help much. So I dig a bit deeper. Apparently dispatch_once is no longer in Swift. As per this accepted answer, I can achieve this by :
let executeStartup = {
self.executeUnsafeStartupWithConfig(config: MPConfig.config.configWithAppKey(appKey: appKey, withAppId: appId, withAccountId: accountId, forProduction: inProduction), authToken: authToken)
}()
_ = executeStartup
But by doing so, I get this warning :
Constant 'executeStartup' inferred to have type '()', which may be
unexpected
So first, is this the correct way of replacing dispatch_once in Swift ? Secondly how do I handle this warning ?
This will definitely execute the block once and you can specify the type as Void so that compiler does not complain.
let executeStartup: Void = {
self.executeUnsafeStartupWithConfig(config: MPConfig.config.configWithAppKey(appKey: appKey, withAppId: appId, withAccountId: accountId, forProduction: inProduction), authToken: authToken)
}()
You can also use lazy var executeStartup: Void as this will also ensure the block is executed once.
Yes this is one of the ways you can replace dispatch_once. For your specific use case you can consider placing this code where it will only be executed once in the lifecycle of the app, which is probably the best approach for your use case.
If you just want to get rid of the warning you can declare the type of executeStartup as Any
let executeStartup : Any = {
self.executeUnsafeStartupWithConfig(config: MPConfig.config.configWithAppKey(appKey: appKey, withAppId: appId, withAccountId: accountId, forProduction: inProduction), authToken: authToken)
}()
I'm using Swift to make a game in SpriteKit.
In Objective-C I could use the following method:
(void)enumerateChildNodesWithName:(NSString *)name usingBlock:(void (^)(SKNode *node, BOOL *stop))block
to perform actions on that *node, but I can't get this function working in Swift. Basically, I don't know how to reference that node in Swift.
This is the code I'm using, but I'm having trouble with the "usingBlock:" part. I've tried many things for many hours, but have not succeeded. Help please!
func spawnEnemy() -> () {
let enemy = SKSpriteNode(imageNamed: "enemy")
enemy.name = "enemy"
enemy.position = CGPointMake(100, 100)
self.addChild(enemy)
}
func checkCollisions() -> () {
self.enumerateChildNodesWithName("enemy", usingBlock: ((SKNode!, CMutablePointer<ObjCBool>) -> Void)?)
}
For now, don't trust autocomplete to insert the code you need — it drops in signatures from the "header", but a block signature is not the same as the declaration you need when inserting your own closure for a block parameter.
The formal way to write a closure would be to replicate the signature inside braces, adding local parameter names and using the in keyword to mark the start of the closure body:
self.enumerateChildNodesWithName("enemy", usingBlock: {
(node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
// do something with node or stop
})
But Swift's type inference means you don't have to write that much. Instead, you can just name the parameters, because their type (as well as the closure's return type) is known:
self.enumerateChildNodesWithName("enemy", usingBlock: {
node, stop in
// do something with node or stop
})
You can also use trailing closure syntax:
self.enumerateChildNodesWithName("enemy") {
node, stop in
// do something with node or stop
}
(You can even drop the local parameter names and refer to parameters by position — e.g. $0 for node — but here isn't a great place to do that because it makes your code far less readable. It's best to reserve $0 and friends for closures where it's blindingly obvious what the parameters are, like the closures you use with map and sort.)
See Closures in The Swift Programming Language for further explanation.
Also, because stop is an UnsafeMutablePointer, the syntax for using it is a bit different than in ObjC: set stop.memory = true to break out of enumeration.
I want to set property value, but getting the error "Cannot assign to the result of this expression".
let operationPageViewController:UIPageViewController = UIPageViewController(transitionStyle: UIPageViewControllerTransitionStyle.Scroll, navigationOrientation: UIPageViewControllerNavigationOrientation.Horizontal, options: nil)
operationPageViewController.viewControllers = ["firstViewController","secondViewController"]
This property is inaccessible or did I do something wrong?
As Christian wrote in the comment, you are supposed to give other arguments like direction, animated, and completion. You need to also make sure that the view controllers that you send are UIViewcontrollers, not string.
func setViewControllers(viewControllers: AnyObject[]!, direction: UIPageViewControllerNavigationDirection, animated: Bool, completion: ((Bool) -> Void)!)
I have a setter method (setMinimumNumberOfSides) that I want to override after using synthesize. In it, I'm putting in a constraint on the instance variable to make sure the int is within certain bounds.
Later in a custom init method, I'm setting another instance variable (numberOfSides), but I need to make sure minimumNumberOfSides and maximumNumberOfSides was set properly within bounds. I tried changing the return value on the setter to a BOOL, so I could pass back a YES or NO if it succeeded/failed, but that created a conflicting method, I'm guessing because I'm using synthesize and overriding the setter.
How can I get the info out easily to check to see if the setter was called and returned successfully?
-(void)setNumberOfSides:(int)sides
{
if ((sides < maximumNumberOfSides) && (sides > minimumNumberOfSides))
{
numberOfSides = sides;
}
else
NSLog (#"Invalid number of sides: %d is outside the constraints allowed", sides);
}
-(void)setMinimumNumberOfSides:(int)minimum
{
if (minimum > 2)
minimumNumberOfSides = minimum;
}
-(void)setMaximumNumberOfSides:(int)maximum
{
if (maximum <= 12)
maximumNumberOfSides = maximum;
}
-(id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max
{
if (self = [super init])
{
self.minimumNumberOfSides = min;
self.maximumNumberOfSides = max;
self.numberOfSides = sides;
}
return self;
}
You don't have to synthesize numberOfSides if you're planning on implementing the getter and setter. Without #synthesize numberOfSides you can return a BOOL if you choose. You'll need to declare the getter/setter in your interface accordingly.
BTW, another approach would be to use the synthesized getter/setter and add a separate method -(BOOL)isNumberOfSidesValid which performs this check.
In a situation like this, you may be better off using a simple call to assert(), or throwing an exception.
The choice will depend on how you see this class being used. If it will be part of a library, and you expect other developers to frequently supply incorrect values for minimumNumberOfSides or maximumNumberOfSides, you should probably throw a proper exception.
A word of warning, though. If you expect the users of your application to frequently supply incorrect values, then an exception is a bad idea. Exception handling in Objective-C is an expensive operation. If these checks are in place for the sake of the user, you should perform input validation, and report errors to the user in a much more friendly manner.
edit: Here is some quick sample code:
-(void)setMinimumNumberOfSides:(int)minimum
{
if (minimum <= 2)
{
[NSException raise:#"invalid minimumNumberOfSides value"
format:#"value of %d is too low (must be > 2)", minimum];
}
minimumNumberOfSides = minimum;
}
edit: Here is another SO question that goes into detail about exception handling in Objective-C.