-
Notifications
You must be signed in to change notification settings - Fork 0
/
params.json
6 lines (6 loc) · 7.55 KB
/
params.json
1
2
3
4
5
6
{
"name": "Swift's Yolo❗️ Operator",
"tagline": "Be safe.",
"body": "What is Swift's Yolo❗️ operator? Simply put, this is the bang ❗️ operator used to force-unwrap optionals. We've all done it -- don't lie. When practicing safe yolo, the Yolo❗️ operator can be a fun, healthy way to write code. So are you practicing safe yolo?\r\n\r\n# Why \"Yolo\"?\r\nWhen an optional variable is force-unwrapped \"unsafely\", we are telling the compiler \"Hey bro, don't worry about this code -- I know better than you do. Yolo. ¯\\\\_(ツ)_/¯\" This is the programming equivalent of \"here, hold my beer -- watch this\". You will get to know this line of code shortly because there is an excellent chance you will see it again in a crash report with the name `EXC_BREAKPOINT`.\r\n\r\n# Types of Yolo❗️\r\nI can see those of you reaching around for your exclamation-shaped pitchforks already. Calm down. As with all things in life, there are exceptions in addition to best practices.\r\n\r\n## 💥 Unsafe Yolo\r\nUnsafe yolo is force-unwrapping optionals because you \"need a non-optional\" or \"this will never be nil\". If it will \"never be nil\", then make it a non-optional -- it's an optional for a reason. Using yolo unsafely should be met with the assumption that when something goes wrong, the \"expected behavior\" is to crash. Good thing you know every single failure path in your application, because you've just opted the compiler out of caring about it.\r\n\r\n## ⚠️ Safe Yolo\r\n\"Safe\" yolo is using force-unwrap in places where the only reason it should crash your application is because of a programming or \"developer\" error. It is a contractual agreement you are entering into with the compiler that you know what's up. A calculated risk.\r\n\r\n## ✅ Mitigated Yolo\r\nMitigated yolo is yolo that has been removed in favor of proper use of either formally optional/non-optional storage. This allows the compiler to completely fact-check your work automatically, at least for expectations around object initialization. Normally these cases require explicit error handling around \"failure\" cases when optionals are nil.\r\n\r\n## 😱 Silent Yolo\r\nMitigated yolo cases in which a formal optional has been used, but your nil failure cases are left entirely un-handled.\r\n\r\n# Yolo❗️ by Example\r\n\r\n## Storyboard Unpacking\r\nLet's get this one out of the way since it's going to come up.\r\n\r\n``` swift\r\n@IBOutlet weak var MyButton: UIButton❗️\r\n```\r\n\r\nWhen you create an IBOutlet from a storyboard or a xib, Xcode will create this little gem.\r\n\r\n⚠️ This is as \"safe\" yolo as you are going to get as the storyboard unpacking system has more knowledge about the system it is building than the compiler does. If these variables are nil when you try and access them, there are only a few possibilities:\r\n\r\n1. You are accessing them before `awakeFromNib` has been called\r\n2. You didn't connect that `IBOutlet` to your storyboard\r\n\r\nThese are both _programmer errors_ and _should_ crash your application.\r\n\r\n## Rogue Optionals\r\n``` swift\r\nfunc completionCallback(optionalObject: Any?, error: ErrorType?) {\r\n guard error == nil else {\r\n // handle the error\r\n return\r\n }\r\n \r\n // do some processing\r\n self.nonOptionalFunc(optional❗️)\r\n}\r\n```\r\n\r\nSomeone (of course not you) has indicated that some variable is \"optional\" and you want to pass it into a function that requires a non-optional parameter.\r\n\r\n✅ Let's mitigate this by using `if let` and explicitly handling both logical branches:\r\n\r\n``` swift\r\nfunc completionCallback(optionalObject: Any?, error: ErrorType?) {\r\n guard error == nil else {\r\n // handle the error\r\n return\r\n }\r\n \r\n if let unwrappedOptional = optional {\r\n // do some processing\r\n self.nonOptionalFunc(unwrappedOptional)\r\n } else {\r\n // explicitly handle this case since something clearly went wrong\r\n }\r\n}\r\n```\r\n\r\n## Inline optionals\r\n``` swift\r\nlet width: CGFloat = self.imageCache.imageForKey(imageKey)❗️.size.width\r\n```\r\nThis is likely the most common occurrence of yolo in your codebase. _Something_ will break here at some point in the future. Go on, do a quick audit of your codebase for `!.`. I'll wait.\r\n\r\n![Many yolos](https://raw.githubusercontent.com/larsacus/swiftyolo.com/gh-pages/yolo-1.png)\r\n\r\nUnfortunately, you're probably not going to like this one very much. Depending on how important or \"required\" the result of the work you are performing, you'll have to handle it in a couple of different ways:\r\n\r\n✅ Explicitly handle the failure\r\n``` swift\r\nlet width: CGFloat\r\nif let cachedImage = self.imageCache.imageForKey(imageKey) {\r\n width = cachedImage.size.width\r\n} else {\r\n // Handle \"failure\" here\r\n}\r\n```\r\n\r\n😱 Silently -- \"don't care yolo\"\r\n``` swift\r\nvar width: CGFloat = 0.0\r\nif let cachedImage = self.imageCache.imageForKey(imageKey) {\r\n width = cachedImage.size.width\r\n}\r\n```\r\n\r\n## Weak Storage\r\n``` swift\r\nweak var delegate: UITableViewDelegate❗️\r\n```\r\nWhen defining storage properties using weak storage, you should _never_\\* yolo these properties. `weak` storage are auto-zeroing properties that can become nil at any time when their owners release them.\r\n\r\nYou are entering into a contract with the compiler that you cannot in any way fulfill without intimate knowledge of the owner of `delegate`.\r\n\r\n✅ This is a no-yolo situation that should be handled correctly with optionals:\r\n\r\n``` swift\r\nweak var delegate: UITableViewDelegate?\r\n```\r\n\r\n## Type Casting\r\n``` swift\r\nlet superObject = (givenObject as❗️ MyObject)\r\n```\r\n\r\n⚠️ This pattern could qualify as an expected cautious-yolo and could be a programmer error depending on your requirements. If your object doesn't get cast correctly, we want it to crash. If you're already at this point, it will likely require much more code to completely obviate the yolo altogether.\r\n\r\n✅ The other option is to `if let` the type cast and handle the \"failure\" depending on your architecture and requirements.\r\n\r\n``` swift\r\nif let superObject = givenObject as? MyObject {\r\n // guaranteed superObject is MyObject and non-optional\r\n}\r\nelse if superObject == nil {\r\n // superObject was nil -- but we didn't crash!\r\n} else {\r\n // givenObject was non-nil , but failed a cast to MyObject -- handle failure depending on requirements\r\n}\r\n```\r\n\r\n## Throwing Errors\r\n``` swift\r\ntry❗️ errorThrowingProcessing(ofObject: obj)\r\n```\r\nEven if this is \"temporary\" code -- both you and I know this will make it into production. You're entering into a contract here where failures that result from this function choking will be met with an application crash.\r\n\r\n✅ Properly handle the error flow\r\n``` swift\r\ndo {\r\n try errorThrowingProcessing(ofObject: obj)\r\n}\r\ncatch {\r\n // Handle error or rethrow -- it was thrown for a reason\r\n}\r\n```\r\n⚠️ The safe yolo path here would be that the application _should_ crash. Instances such as unpacking a resource from the bundle. If it doesn't exist, the bundle has in some way been damaged.\r\n``` swift\r\nlet resource = try❗️ unpackCriticalBundledResourceWithName(resourceName)\r\n```\r\n\r\n\r\n_\\* unless you seriously know what you're doing and have written a lot of documentation around this code. judicious use of emoji is recommended with this type of documentation_",
"note": "Don't delete this file! It's used internally to help with page regeneration."
}