Swift Exceptions to Exception handling - exception

After perusing through forums and Swift documentation (not completely, I admit), it appears that instead of try-catch mechanisms, in Swift we are encouraged to write code that is more safe from exceptions. In light of that, I have a question about a sample API, and would like to learn how to more safely handle this situation:
For example, I can create the following class using the NSDecimalNumberHandler:
class MathWhiz {
init() {
let defaultBehavior: NSDecimalNumberHandler =
NSDecimalNumberHandler.defaultDecimalNumberHandler()
}
func add(op1: String, op2: String) ->NSDecimalNumber {
return NSDecimalNumber.decimalNumberWithString(op1).decimalNumberByAdding(NSDecimalNumber.decimalNumberWithString(op2))
}
}
If I use the following, I get a number:
let brain = MathWhiz()
brain.add("1", op2: "1e127")
However, if I cause an overflow exception,:
brain.add("1", op2: "1e128")
I will crash the program as expected.
So, my question is, the API raises exceptions, but I don't handle them here. There are other posts out there of people pointing out that Swift does not have exception handling, but this question is seeking a nice way to handle this problem in the way that the language creators were thinking it should be done.
Is there a recommended way to handle this without having to write my own code to check for overflow, underflow, loss of precision, ect...? I am wanting the NSDecimalNumberHandler to do that for me.

If you are designing a function (or method) in Swift, you have at least 3 choices for dealing with errors:
Choice 1: Return an Optional Type
If your function might fail, and this happens on a regular basis, then consider returning an optional type variable. For example, in your case, your method add could return an NSDecimalNumber? instead of a plain NSDecimalNumber. In that case, your method would check for everything that could go wrong, and return nil in those situations. Overflow and underflow would return nil, and all other cases would return an NSDecimalNumber. The callers would have to check for and unwrap the optional NSDecimalNumber like this:
let brain = MathWhiz()
if let sum = brain.add("1", op2: "1e127") {
println("the result was \(sum)")
} else
println("something went wrong with MathWhiz add")
}
Choice 2: Return an Enumerated Type
If you want to return more information about the thing that went wrong, you could create an enumerated type with a value for each error and one for success that embeds the answer. For example, you could do:
enum MathWhizResult {
case Overflow
case Underflow
case Success(NSDecimalNumber)
}
Then add would be defined to return MathWhizResult:
func add(op1: String, op2: String) -> MathWhizResult
In the case of an error, add would return .Overflow or .Underflow. In the case of success, add would return Success(result). The caller would have to check the enumeration and unpack the result. A switch could be used for this:
switch (brain.add("1", op2: "1e128")) {
case .Overflow
println("darn, it overflowed")
case .Underflow
println("underflow condition happened")
case .Success(let answer)
println("the result was \(answer)"
}
Choice 3: Choose not to handle errors explicitly
Unpacking the result in the first two choices might be too much overhead for an error that is very rare. You could chose to just return a result, and let the caller deal with the possibility of an underflow or overflow condition. In that case, they would have to check for these conditions themselves before calling add. The benefit is, it they know that their program will never cause an underflow or overflow (because they are dealing with single digit numbers for instance), they are not burdened with unpacking the result.
I created a small app to demonstrate how you could do this with NSDecimalNumbers. I created a Single View Application in Xcode. In the ViewController in the StoryBoard I added 3 TextFields (one each for operand 1, operand 2, and the result) and a Button that I labelled +.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var operand1 : UITextField!
#IBOutlet var operand2 : UITextField!
#IBOutlet var result : UITextField!
var brain = MathWhiz()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addButton(sender : UIButton) {
var op1 = operand1.text
var op2 = operand2.text
// Perform the add with the contents of the operand fields.
// Print the answer, or "No Result" if add returns nil.
if let answer = brain.add(op1, op2: op2)?.description {
result.text = answer
} else {
result.text = "No Result"
}
}
}
MathWhiz.swift
import UIKit
// Declare that we implement NSDecimalNumberBehaviors so that we can handle
// exceptions without them being raised.
class MathWhiz: NSDecimalNumberBehaviors {
var badException = false
// Required function of NSDecimalNumberBehaviors protocol
func roundingMode() -> NSRoundingMode {
return .RoundPlain
}
// Required function of NSDecimalNumberBehaviors protocol
func scale() -> CShort {
return CShort(NSDecimalNoScale)
}
// Required function of NSDecimalNumberBehaviors protocol
// Here we process the exceptions
func exceptionDuringOperation(operation: Selector, error: NSCalculationError, leftOperand: NSDecimalNumber, rightOperand: NSDecimalNumber) -> NSDecimalNumber? {
var errorstr = ""
switch(error) {
case .NoError:
errorstr = "NoError"
case .LossOfPrecision:
errorstr = "LossOfPrecision"
case .Underflow:
errorstr = "Underflow"
badException = true
case .Overflow:
errorstr = "Overflow"
badException = true
case .DivideByZero:
errorstr = "DivideByZero"
badException = true
}
println("Exception called for operation \(operation) -> \(errorstr)")
return nil
}
// Add two numbers represented by the strings op1 and op2. Return nil
// if a bad exception occurs.
func add(op1: String, op2: String) -> NSDecimalNumber? {
let dn1 = NSDecimalNumber(string: op1)
let dn2 = NSDecimalNumber(string: op2)
// Init badException to false. It will be set to true if an
// overflow, underflow, or divide by zero exception occur.
badException = false
// Add the NSDecimalNumbers, passing ourselves as the implementor
// of the NSDecimalNumbersBehaviors protocol.
let dn3 = dn1.decimalNumberByAdding(dn2, withBehavior: self)
// Return nil if a bad exception happened, otherwise return the result
// of the add.
return badException ? nil : dn3
}
}

Well, you're using an ObjC API. So just handle the exceptions inside ObjC. Write an ObjC class that handles the exceptions and returns values for Swift code to consume.
One possibility would be to write MathWhiz as an ObjC class and return an inout NSError parameter (ie do it the way Core Data does, take an **NSError) and fill it with the proper value when you hit a recoverable error. Then you can eat exceptions from NSDecimalNumber and convert them into NSError values.
You could also write an entire NSDecimalNumber wrapper for Swift consumption that you then use in place of NSDecimalNumber in your Swift code. Perhaps you could overload operator + and its siblings for this class, and then work out how to represent the various possible errors without exceptions.

update answer for swift 2.0
As you mentioned, now Swift 2.0 support with try, throw, and catch keywords.
here is official announcement.
Error handling model: The new error handling model in Swift 2.0 will
instantly feel natural, with familiar try, throw, and catch keywords.
Best of all, it was designed to work perfectly with the Apple SDKs and
NSError. In fact, NSError conforms to a Swift’s ErrorType. You’ll
definitely want to watch the WWDC session on What’s New in Swift to
hear more about it.
e.g
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}
}

Related

Kotlin Serialization for Generic Objects...?

I had the bright idea to implement a generic object and attempt to serialize / deserialize it, and received this error:
Serializer for class 'DetailsRequest' is not found.
Mark the class as #Serializable or provide the serializer explicitly.
I thought the #Serializer annotation would have achieved this... ???
The implementation of the data class, and its custom serializer (which is probably overkill), are as follows:
#Serializable(with=DetailsRequestSerializer::class)
data class DetailsRequest<out T>(val details: T)
object DetailsRequestSerializer : KSerializer<DetailsRequest<*>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(
"DetailsRequest") {
when (this::class) {
String::class -> element<String>("details")
Long::class -> element<Long>("details")
Int::class -> element<Int>("details")
Double::class -> element<Double>("details")
Float::class -> element<Float>("details")
else -> element<String>("details")
}
}
override fun serialize(
encoder: Encoder,
value: DetailsRequest<*>
) {
value.details.let {
encoder.encodeStructure(descriptor) {
when (value::class) {
String::class -> encodeStringElement(descriptor, 0, value.details as String)
Long::class -> encodeLongElement(descriptor, 0, value.details as Long)
Int::class -> encodeIntElement(descriptor, 0, value.details as Int)
Double::class -> encodeDoubleElement(descriptor, 0, value.details as Double)
Float::class -> encodeFloatElement(descriptor, 0, value.details as Float)
else -> encodeStringElement(descriptor, 0, value.details as String)
}
}
}
}
override fun deserialize(decoder: Decoder): DetailsRequest<*> {
return when (this::class) {
String::class -> DetailsRequest(decoder.decodeString())
Long::class -> DetailsRequest(decoder.decodeLong())
Int::class -> DetailsRequest(decoder.decodeInt())
Double::class -> DetailsRequest(decoder.decodeDouble())
Float::class -> DetailsRequest(decoder.decodeFloat())
else -> DetailsRequest(decoder.decodeString())
}
}
}
I have written this unit test:
class PlaygroundTest {
private val json = Json {
encodeDefaults = true
isLenient = true
allowSpecialFloatingPointValues = true
allowStructuredMapKeys = true
prettyPrint = true
useArrayPolymorphism = false
ignoreUnknownKeys = true
}
#Test
fun `Details Integration Tests`() {
val input = DetailsRequest(details = "New Record")
val value = json.encodeToString(input)
println("value=[$value]")
val output = value.convertToDataClass<DetailsRequest<String>>()
println("output=[$output]")
}
#OptIn(InternalSerializationApi::class)
internal inline fun <reified R : Any> String.convertToDataClass() =
json.decodeFromString(R::class.serializer(), this)
}
Bonus points: This is a Kotlin Multiplatform project, which I do not believe will impact this situation.
Is this type of situation even possible?
The problem you asked about
Good news: this situation is totally possible, and your code is very nearly correct. Kotlin's builtin serialization is a bit wonky about getting serializers, though, so it is a bit surprising at first that your code snippet doesn't work.
The answer lies in a bit of almost fine print in the documentation[1]:
Constraints
This paragraph explains known (but not all!) constraints of the serializer() implementation. Please note that they are not bugs, but implementation restrictions that we cannot workaround.
[...]
Serializers for classes with generic parameters are ignored by this method
Since DetailsRequest<out T> has that generic out T, this means that accessing its serializer through DetailsRequest::class.serializer() won't work.
Fortunately, kotlinx.serialization provides us a different method[2] to get a serializer for an arbitrary type:
inline fun <T> serializer(): KSerializer<T>
In your case, you could use it like this:
// OptIn no longer needed
internal inline fun <reified R : Any> String.convertToDataClass() =
json.decodeFromString(serializer<R>(), this)
Note that there's an overload of json.decodeFromString that uses serializer() automatically: all you really need is
internal inline fun <reified R : Any> String.convertToDataClass() =
json.decodeFromString<R>(this)
(In fact, at this point, you may even consider removing the convertToDataClass method entirely.)
The problem you didn't ask about
If you run the code with that change, you'll get a different error:
Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Expected beginning of the string, but got {
JSON input: {
"details": "New Record"
}
This is simply because your implementation of the deserializer is a bit off—it's trying to deserialize DetailsRequest as a single string (such as "New Record") rather than a full JSON object the way you serialized it (such as { "details": "New Record" }).
A full explanation of the issue here would take a bit more space than is reasonable for this answer, but I refer you to the very complete—if a bit long—serialization guide here. You may need to scroll up and read the rest of the Custom Serializers section to ensure you understand it all as well.
A quicker solution
If you mark DetailsRequest with simply #Serializable (without the with argument) and remove the entire DetailsRequestSerializer object, the code will run fine and serialize and deserialize as one would expect. The #Serializable annotation (without a with argument) tells the compiler to autogenerate a serializer, which it does successfully and reasonably in this case.
Unless you have a specific serialization need that this default serializer does not fulfill, I would strongly recommend simply using the default one. It's much cleaner and a lot less work. :-)
[1] You'll have to scroll down a bit; Dokka doesn't allow me to link to a specific declaration of a function. The relevant bit is all the way at the bottom.
[2] Yes, it's on the same page in the documentation. Just don't scroll down this time. :-)

Initialize subclasses from json

I am looking for a good way of persisting arbitrary subclasses.
I am writing objects asDictionary to json upon save, and init(json) them back upon load. The structure is Groups that have Units of different kind. The Group only knows its units as something implementing UnitProtocol.
The subclasses UA etc of Unit has the exact same data as a Unit. So data wise, the asDictionary and init(json) fits well in Unit. The subclasses only differ in logic. So when restoring them from file, I believe it has to be the exact subclass that is initialized.
(Bad) Solutions I thought of
Letting every group know that it can have Units of different subclasses by not holding them as [UnitProtocol] only, but also as [UA], [UB], etc, that can saved separately, restored by their respective sub inits, and be merged into a [UnitProtocol] upon init.
Store subunits with their classname and create a Unit.Init(json) that somehow is able to pass down the initialization depending on subtype.
?? Still thinking, but I believe there has to be something I can learn here to do this in a maintainable way without breaking the single responsibility policy.
For init class from json I used this technic :
//For row in json
for row in json {
//namespace it use for init class
let namespace = (Bundle.main.infoDictionary!["CFBundleExecutable"] as! String).replacingOccurrences(of: " ", with: "_")
//create instance of your class
if let myClass = NSClassFromString("\(namespace).\(row.name)") as? NameProtocol.Type{
//init your class
let classInit : NameProtocol = myClass.init(myArgument: "Hey")
//classInit is up for use
}
}
Store every Unit json with its classname
func asDictionary() -> Dictionary<String, Any> {
let className = String(describing: type(of: self))
let d = Dictionary<String, Any>(
dictionaryLiteral:
("className", className),
("someUnitData", someUnitData),
// and so on, for all the data of Unit...
And init from json with #horusdunord's solution:
for unitJson in unitsJson {
let className = (unitJson as! [String: Any])["className"] as? String
let namespace = (Bundle.main.infoDictionary!["CFBundleExecutable"] as! String).replacingOccurrences(of: " ", with: "_")
if let unitSubclass = NSClassFromString("\(namespace).\(className ?? "N/A")") as? UnitProtocol.Type {
if let unit = unitSubclass.init(json: unitJson as! [String : Any]) {
units.append(unit)
} else {
return nil
}
} else {
return nil
}
}
The trick here is that casting the class unitSubclass to UnitProtocol then allows you to call its init(json) declared in that protocol but implemented in the particular subclass, or in its superclass Unit, if the properties are all the same.

Swift 3 : Call can throw, but it is not marked with 'try' and the error is not handled [duplicate]

After I installed Xcode 7 beta and convert my swift code to Swift 2, I got some issue with the code that I can't figure out. I know Swift 2 is new so I search and figure out since there is nothing about it, I should write a question.
Here is the error:
Call can throw, but it is not marked with 'try' and the error is not
handled
Code:
func deleteAccountDetail(){
let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
let request = NSFetchRequest()
request.entity = entityDescription
//The Line Below is where i expect the error
let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]
for entity in fetchedEntities {
self.Context!.deleteObject(entity)
}
do {
try self.Context!.save()
} catch _ {
}
}
Snapshot:
You have to catch the error just as you're already doing for your save() call and since you're handling multiple errors here, you can try multiple calls sequentially in a single do-catch block, like so:
func deleteAccountDetail() {
let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
let request = NSFetchRequest()
request.entity = entityDescription
do {
let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]
for entity in fetchedEntities {
self.Context!.deleteObject(entity)
}
try self.Context!.save()
} catch {
print(error)
}
}
Or as #bames53 pointed out in the comments below, it is often better practice not to catch the error where it was thrown. You can mark the method as throws then try to call the method. For example:
func deleteAccountDetail() throws {
let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
let request = NSFetchRequest()
request.entity = entityDescription
let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]
for entity in fetchedEntities {
self.Context!.deleteObject(entity)
}
try self.Context!.save()
}
When calling a function that is declared with throws in Swift, you must annotate the function call site with try or try!. For example, given a throwing function:
func willOnlyThrowIfTrue(value: Bool) throws {
if value { throw someError }
}
this function can be called like:
func foo(value: Bool) throws {
try willOnlyThrowIfTrue(value)
}
Here we annotate the call with try, which calls out to the reader that this function may throw an exception, and any following lines of code might not be executed. We also have to annotate this function with throws, because this function could throw an exception (i.e., when willOnlyThrowIfTrue() throws, then foo will automatically rethrow the exception upwards.
If you want to call a function that is declared as possibly throwing, but which you know will not throw in your case because you're giving it correct input, you can use try!.
func bar() {
try! willOnlyThrowIfTrue(false)
}
This way, when you guarantee that code won't throw, you don't have to put in extra boilerplate code to disable exception propagation.
try! is enforced at runtime: if you use try! and the function does end up throwing, then your program's execution will be terminated with a runtime error.
Most exception handling code should look like the above: either you simply propagate exceptions upward when they occur, or you set up conditions such that otherwise possible exceptions are ruled out. Any clean up of other resources in your code should occur via object destruction (i.e. deinit()), or sometimes via defered code.
func baz(value: Bool) throws {
var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
var data = NSData(contentsOfFile:filePath)
try willOnlyThrowIfTrue(value)
// data and filePath automatically cleaned up, even when an exception occurs.
}
If for whatever reason you have clean up code that needs to run but isn't in a deinit() function, you can use defer.
func qux(value: Bool) throws {
defer {
print("this code runs when the function exits, even when it exits by an exception")
}
try willOnlyThrowIfTrue(value)
}
Most code that deals with exceptions simply has them propagate upward to callers, doing cleanup on the way via deinit() or defer. This is because most code doesn't know what to do with errors; it knows what went wrong, but it doesn't have enough information about what some higher level code is trying to do in order to know what to do about the error. It doesn't know if presenting a dialog to the user is appropriate, or if it should retry, or if something else is appropriate.
Higher level code, however, should know exactly what to do in the event of any error. So exceptions allow specific errors to bubble up from where they initially occur to the where they can be handled.
Handling exceptions is done via catch statements.
func quux(value: Bool) {
do {
try willOnlyThrowIfTrue(value)
} catch {
// handle error
}
}
You can have multiple catch statements, each catching a different kind of exception.
do {
try someFunctionThatThowsDifferentExceptions()
} catch MyErrorType.errorA {
// handle errorA
} catch MyErrorType.errorB {
// handle errorB
} catch {
// handle other errors
}
For more details on best practices with exceptions, see http://exceptionsafecode.com/. It's specifically aimed at C++, but after examining the Swift exception model, I believe the basics apply to Swift as well.
For details on the Swift syntax and error handling model, see the book The Swift Programming Language (Swift 2 Prerelease).

Overloading a function in go doesn't work

I have a function which currently doesn't receive a bool parameter, but then calls another function with a hardcoded bool. We need to remove the hardcoded call and allow a bool to be passed.
I first thought I could try some default parameter - my google searches resulted in that Go apparently doesn't support optional (resp. default) parameter.
So I thought I'd try function overloading.
I found this thread on reddit, which says that it works with a special directive since version 1.7.3:
https://www.reddit.com/r/golang/comments/5c57kg/when_did_function_overloading_get_slipped_in/
I am using 1.8, and still I couldn't get it to work.
I am not even sure I may be allowed to use that directive, but I was speculating that changing the function signature right away may be dangerous as I don't know who uses the function...
Anyway - even with //+overloaded it didn't work
Is there any "idiosyncratic" way or pattern to solve this problem in Go?
//some comment
//+overloaded
func (self *RemoteSystem) Apply(rpath, lpath string, dynamic bool) error {
result, err := anotherFunc(rpath, dynamic)
}
//some comment
//+overloaded
func (self *RemoteSystem) Apply(rpath, lpath string ) error {
//in this function anotherFunc was being called, and dynamic is hardcoded to true
//result, err := anotherFunc(rpath, true)
return self.Apply(rpath, lpath, true)
}
When I run my test, I get (forgive me for omitting part of the real path to file):
too many arguments in call to self.Apply
have (string, string, bool)
want (string, string)
../remotesystem.go:178: (*RemoteSystem).Apply redeclared in this block
previous declaration at ../remotesystem.go:185
Overloading isn't available in Go. Instead of writing functions with the same name that do different things, it is preferable to be more expressive with what the function does in the function name. In this instance, what would commonly be done is something like this:
func (self *RemoteSystem) Apply(rpath, lpath string, dynamic bool) error {
result, err := anotherFunc(rpath, dynamic)
}
func (self *RemoteSystem) ApplyDynamic(rpath, lpath string ) error {
//in this function anotherFunc was being called, and dynamic is hardcoded to true
return self.Apply(rpath, lpath, true)
}
Just by the name of the function, you can easily tell what is different and why.
Another example to provide some context (pun intended).
I write a lot of Google App Engine code in Go using go-endpoints. The way to log things is different depending on if you have a context or not. My logging functions ended up like this.
func LogViaContext(c context.Context, m string, v ...interface{}) {
if c != nil {
appenginelog.Debugf(c, m, v...)
}
}
func LogViaRequest(r *http.Request, m string, v ...interface{}) {
if r != nil {
c := appengine.NewContext(r)
LogViaContext(c, m, v...)
}
}
From the Reddit post:
Unicode. I can tell by the pixels.
Go doesn't support function overloading. But it does support using Unicode characters in function names, which allows you to write function names that look like other function names.
The first one is setValue, the second one is setV\u0430lue aka setV\xd0\xb0lue (with CYRILLIC SMALL LETTER A) and the third is setVal\U0001d69ee aka setVal\xf0\x9d\x9a\x9ee (with MATHEMATICAL MONOSPACE SMALL U).
See also:
Does the Go language have function/method overloading? (stackoverflow.com)
Why does Go not support overloading of methods and operators? (golang.org)
Alternative for function overloading in Go? (stackoverflow.com)

What are the differences between functions and methods in Swift?

I always thought functions and methods were the same, until I was learning Swift through the "Swift Programming Language" eBook. I found out that I cannot use greet("John", "Tuesday") to call a function that I declared inside a class, as shown in the eBook in the screen shot below:
I received a error saying that "Missing argument label 'day:' in call" as per this screen shot:
Here is the code:-
import Foundation
import UIKit
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//var dailyStatement = greet("John", "Tuesday")
var dailyStatement = greet("John", day: "Tuesday")
println(dailyStatement)
}
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
}
After some research, I found this post: Difference between a method and a function, and it seems to me that the function that I declared inside a class is actually called a method. So, the syntax that I use to call the method is different compared to the syntax that I use to call a function.
I never realized this difference when I was programming in Objective-C.
What are the differences between functions and methods in Swift?
When do we use functions and when do we use methods in Swift?
After a few hours of reading and experimenting, here are the things that I found out:-
Functions in Swift
Functions are self-contained chunks of code that perform a specific
task. You give a function a name that identifies what it does, and
this name is used to “call” the function to perform its task when
needed.
Resource: Official Apple Documentation on Functions in Swift
Function Parameter Names
However, these parameter names are only used within the body of the
function itself, and cannot be used when calling the function. These
kinds of parameter names are known as local parameter names, because
they are only available for use within the function’s body.
It means that by default, all the parameters for Function are local parameters.
But, sometimes we want to indicate the purpose of each parameter. So, we can actually define an external parameter name for each parameter. Example Code:
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
Another way to make the external parameter name is using hash symbol (#) to shorten the name.
func someFunction(#localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
To call the above functions with external parameter, you may use
someFunction(localParameterName:10)
Methods in Swift
Methods are functions that are associated with a particular type.
Classes, structures, and enumerations can all define instance methods,
which encapsulate specific tasks and functionality for working with an
instance of a given type.
Resource: Official Apple Documentation on Methods in Swift
However, the default behavior of local names and external names is
different for functions and methods.
Specifically, Swift gives the first parameter name in a method a local
parameter name by default, and gives the second and subsequent
parameter names both local and external parameter names by default.
Code below shows the differences for default and non-default parameters for method in Swift.
import Foundation
import UIKit
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Default methods calling
var dailyStatement = greet("Rick", day: "Tuesday")
println(dailyStatement)
//First parameter is also an external parameter
var dailyStatement2 = greet2(name:"John", day: "Sunday")
println(dailyStatement2)
}
//Default: First Parameter is the local parameter, the rest are external parameters
func greet (name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
//Use Hash symbol to make the First parameter as external parameter
func greet2 (#name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
}
I might miss some important details. Hope someone can provide a better answer.
As you said yourself, methods are functions, but in a class. In objective-c you never realized this, because we were only coding in classes. Every function that we wrote was a method of a class (ViewController or some other class we created).
In Swift we have the ability to create functions that are not inside some class. The main reason for doing this is to write functions that are not tied to any class, and can be used wherever we need them. So if you have a function that is related to a class you write it inside the class and you can access is from every instance of the class:
class Square {
var length: Double
func area() -> Double {
return length * length
}
}
But if you need to access the function from everywhere, then you don't write it inside a class. For example:
func squared(number: Int) -> Int {
return number * number
}
About your syntax issues between functions and methods: You guessed it right, methods and functions are called a little bit differently. That is because in Objective-C we had long method names and we liked them because we could read what the methods were doing and what the parameters were for. So the first parameter in a method is in most cases described by the function name itself. And the other parameters shouldn't only be some numbers or strings or instances, they should be described as well, so Swift writes the name of the variable automatically. If you want to describe it by yourself you can do that as well:
class Something {
func desc(firstString string1: String, secondString string2:String) {...}
}
Well, #Ricky's answer says it pretty much. I was confused what exactly they are. So here is my thought:
Functions could be defined outside of classes or inside of classes/structs/enums, while Methods have to be defined inside of and part of classes/structs/enums.
We could define a Function outside of any Type's definition and could use it within Methods of any Type's definition.
Just my understanding and illustration here, hope this helps someone else or you may edit if you feel there is an improvement needed OR let me know if anything is wrong:
//This is a Function which prints a greeting message based on the category defined in an 'enum'
func greet(yourName name: String, category: GreetingsCategory) {
switch category {
case .Person:
print("Hello, " + name + " Today is Tuesday")
case .Vehicle:
print("Hello, " + name + " your Vehicle is a Car")
}
}
//This is an 'enum' for greetings categories
enum GreetingsCategory: String {
case Person
case Vehicle
}
//Type: Person
class Person {
//This is a method which acts only on Person type
func personGreeting() {
greet(yourName: "Santosh", category: .Person)
}
}
//Type: Vehicle
class Vehicle {
//This is a method which acts only on Vehicle type
func vehicleGreeting() {
greet(yourName: "Santosh", category: .Vehicle)
}
}
//Now making use of our Function defined above by calling methods of defferent types.
let aPerson = Person()
aPerson.personGreeting()
//prints : Hello, Santosh Today is Tuesday
let aVehicle = Vehicle()
aVehicle.vehicleGreeting()
//prints: Hello, Santosh your Vehicle is a Car
//We can also call the above function directly
greet(yourName: "Santosh", category: .Person)
Mainly the names are used interchangeably without people having a real intent of distinguishing them. But ultimately they do have a difference.
someFile.swift:
func someFunc{
//some code
}
class someClass{
func someMethod{
//some code
}
}
Note: someClass != someFile
someMethod works only on its associated type which is 'someClass'. However the same can't be said for someFunc. someFunc is only in the someClass.Swift because semantically it is better suited to be written in that file. It could have been written in any other class as long as it's marked with private
And obviously the method can access self. With functions, there is no self.. For more see: What's the difference between a method and a function?
Here is a simple answer on the difference between functions and methods:
Some folks use “function” and “method” interchangeably, but there’s a
small difference: both of them are reusable chunks of code, but
methods belong to classes, structs, and enums, whereas functions do
not.
So:
func thisIsAFunction() {
}
struct Person {
func thisIsAMethod() {
}
}
Because methods always belong to a data type, they have a concept of
self that functions do not.
source: https://www.hackingwithswift.com/example-code/language/whats-the-difference-between-a-function-and-a-method
Lots of great answers, but let me use Xcode to show something visually from the UIKit module:
That is a function because it's written at the global level. It's not a method. Methods are scoped to a class.
Screenshot to show that it's at the global level.
The following function is at the global level:
public func UIApplicationMain(_ argc: Int32, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>!,
_ principalClassName: String?, _ delegateClassName: String?) -> Int32
Icons for the different symbols. (Class, Method, Property, Protocol, Function, Extensions are all different symbols)
The function has an icon like 𝓯
The method has an icon of M
functional principle as a part of functional language
function is a first-class type (first-class citizen) in Swift. Higher order functions
assign to a variable
pass as an argument
return
Function
Function is a block of code that is created for executing some task. Function consists of name, optional parameters(name, type), optional return type, body.
func name(parameterName1: Int, parameterName2: String) -> Bool {
//statements
return true
}
Function type - function’s parameter type + return type[Java about]. For example it is used as function's parameter
//Function type for the sample above
(Int, String) -> Bool
Method
Method - is a function which is associated with a type - class, structure, enum [About]:
Instance method - method which belongs to instance
MyClass().foo()
Type method - method which belongs to type itself. class or static is used[About]
MyClass.foo()
Closure
As official doc says that Closure in Swift has three next forms:
global function(with name, without capturing) - is a function that is declared in a global scope(out of class scope). Usually it is defined as a first level of .swift file and does not have a big memory foot print
nested function(with name, with capturing enclosing function variables) - function inside other function
closure expression(without name, with capturing enclosing context)
Closure(closure expression) - anonymous function - is a block of code(functionality). Closure is a type of function without name. Closure is a function in terms of Functional programming. It can support capturing concept(if it doesn't capture it is lambda). It is similar to block in Objective-C.
[Closure vs Lambda]
They can be used for:
non-escaping closure - sync operations - click events, sort...
escaping closure - async operations - e.g.completion handler - it is a callback/notification which is called when task is done
class ClassA {
var variable = "Hello"
func fooA() {
print(variable)
let b = ClassB() //1
b.fooB(completionHandler: { [weak self] (input) -> String? in //2 pass closure //3 capture list or any calls from closure to outher scope
guard let self = self else { return nil }
self.variable = "World" //capturing self.variable
return String(input)
})
}
}
class ClassB {
var myCompletionHandler: ((Int) -> String?)? = nil //5
func fooB(completionHandler: #escaping (Int) -> String?) { //4
self.myCompletionHandler = completionHandler //6
let result = completionHandler(7)
}
func fooB2(completionHandler: #escaping (Int) -> String?) { //if you re-pass function to #escaping function you must mark it by #escaping too
self.fooB(completionHandler: completionHandler)
}
}
func testClosure() {
ClassA().fooA()
}
(Int) -> String? //Function type
//Closure Expression
{ [weak self] (input) -> String? in
//logic
}
[non-escaping vs escaping closure]
[#autoclosure]
[Init/customize stored property by closure]
[JVM Memory model]