On this article I’m going to introduce you to the basics of the Swift programming language in about one hour.
This text is aimed toward two varieties of individuals: people who’ve accomplished the introduction to my 100 Days of SwiftUI course and are in search of a fast overview, and people who’re skilled with different languages and need to switch their abilities over to Swift.
We’re going to maneuver quick as a result of that is designed to be a primer – if you end up struggling to grasp one thing, go to the 100 Days of SwiftUI to finish the longer, extra detailed introduction there.
Let’s get to it!
Creating constants and variables
Swift can create constants and variables, however constants are usually preferable.
Use this to create then change a variable string:
var identify = "Ted" identify = "Rebecca"
In the event you don’t need to change a price, use a fixed as a substitute:
let person = "Daphne"
print() perform is useful for studying and debugging, and reveals some details about a variable:
Swift’s strings begin and finish with double quotes:
let actor = "Tom Cruise"
They usually work nice with emoji too:
let actor = "Tom Cruise 🏃♂️"
If you need double quotes inside your string, place a backslash earlier than them:
let quote = "He tapped an indication saying "Consider" and walked away."
If you need a string that spans a number of strains, begin and finish with three double quotes, like this:
let film = """ A day in the lifetime of an Apple engineer """
Swift gives many helpful properties and strategies for strings, together with
.rely to learn what number of letters it has:
There are additionally
hasSuffix(), which lets us know whether or not a string begins or ends with particular letters:
Necessary: Strings are case-sensitive in Swift, in order that second examine will return false.
Swift shops complete numbers utilizing the kind
Int, which helps a variety of normal mathematical operators:
let rating = 10 let higherScore = rating + 10 let halvedScore = rating / 2
It additionally helps compound task operators that modify variables in place:
var counter = 10 counter += 5
Integers include their very own helpful performance, such because the
let quantity = 120 print(quantity.isMultiple(of: 3))
You can even make random integers in a selected vary, like this:
let id = Int.random(in: 1...1000)
In the event you create a quantity with a decimal level, Swift will take into account it a
let rating = 3.0
Double to be an entirely completely different sort of knowledge to
Int, and gained’t allow you to combine them collectively.
Swift makes use of the kind
Bool to retailer true or false:
let goodDogs = true let gameOver = false
You’ll be able to flip a Boolean from true to false by calling its
var isSaved = false isSaved.toggle()
Becoming a member of strings
You’ll be able to create strings out of different information utilizing string interpolation: write a backslash inside your string, then place the identify of a variable or fixed inside parentheses, like this:
let identify = "Taylor" let age = 26 let message = "I am (identify) and I am (age) years previous." print(message)
When that code runs, it should print “I’m Taylor and I’m 26 years previous.”
You’ll be able to group objects into an array like this:
var colours = ["Red", "Green", "Blue"] let numbers = [4, 8, 15, 16] var readings = [0.1, 0.5, 0.8]
Every of these maintain completely different varieties of knowledge: one strings, one integers, and one decimals. After we learn information from arrays we’ll get the suitable sort again – a
Int, or a
Tip: Ensure that an merchandise exists on the index you’re asking for, in any other case your code will crash – your app will simply cease working.
In case your array is variable, you should utilize
append() so as to add new objects:
The kind of information you add should match no matter was already in there.
Arrays have helpful performance, reminiscent of
.rely to learn what number of objects are in an array, or
take away(at:) to take away one merchandise at a selected index:
colours.take away(at: 0) print(colours.rely)
You’ll be able to examine whether or not an array comprises a specific merchandise through the use of
comprises(), like this:
Dictionaries retailer a number of values in line with a key we specify. For instance, we might create a dictionary to retailer details about an individual:
let worker = [ "name": "Taylor", "job": "Singer" ]
To learn information from the dictionary, use the identical keys you used when creating it:
print(worker["name", default: "Unknown"]) print(worker["job", default: "Unknown"])
default worth will probably be used if the important thing we’ve requested for doesn’t exist.
Units are just like arrays, besides you possibly can’t add duplicate objects, and so they don’t retailer objects in a specific order.
This makes a set of numbers:
var numbers = Set([1, 1, 3, 5, 7]) print(numbers)
Bear in mind, the set will ignore duplicate values, and it gained’t bear in mind the order used within the array.
Including objects to a set is completed by calling its
insert() methodology, like this:
Units have one large benefit over arrays: utilizing
comprises() on a set is successfully instantaneous irrespective of what number of objects the set comprises – even a set with 10,000,000 objects will reply immediately.
An enum is a set of named values we are able to create and use to make our code extra environment friendly and safer. For instance, we might make an enum of weekdays like this:
enum Weekday case monday, tuesday, wednesday, thursday, friday
That calls the brand new enum
Weekday, and gives 5 instances to deal with the 5 weekdays.
We are able to now make situations of that enum, then assign different doable instances to it:
var day = Weekday.monday day = .friday
You’ll be able to attempt to drive a selected sort for a brand new variable or fixed through the use of sort annotation like this:
var rating: Double = 0
With out the
: Double half Swift would infer that to be an
Int, however we’re overriding that and saying it’s a
Listed below are some sort annotations based mostly on the kinds coated to date:
let participant: String = "Roy" var luckyNumber: Int = 13 let pi: Double = 3.141 var isEnabled: Bool = true var albums: Array<String> = ["Red", "Fearless"] var person: Dictionary<String, String> = ["id": "@twostraws"] var books: Set<String> = Set(["The Bluest Eye", "Foundation"])
Arrays and dictionaries are so widespread that they’ve particular syntax that’s simpler to write down:
var albums: [String] = ["Red", "Fearless"] var person: [String: String] = ["id": "@twostraws"]
Realizing the precise varieties of issues is necessary for creating empty collections. For instance, each of those create empty string arrays:
var groups: [String] = [String]() var clues = [String]()
Values of an enum have the identical sort because the enum itself, so we might write this:
enum UIStyle case mild, darkish, system var type: UIStyle = .mild
else if statements to examine a situation and run some code as applicable:
let age = 16 if age < 12 print("You'll be able to't vote") else if age < 18 print("You'll be able to vote quickly.") else print("You'll be able to vote now.")
We are able to use
&& to mix two situations, and the entire situation will solely be true if the 2 components inside are true:
let temp = 26 if temp > 20 && temp < 30 print("It is a good day.")
|| will make a situation be true if both subcondition is true.
Swift lets us examine a price towards a number of situations utilizing
case syntax, like this:
enum Climate case solar, rain, wind let forecast = Climate.solar change forecast case .solar: print("A pleasant day.") case .rain: print("Pack an umbrella.") default: print("Needs to be okay.")
change statements should be exhaustive: all doable values have to be dealt with so you possibly can’t miss one accidentally.
The ternary conditional operator
The ternary operator lets us examine a situation and return one in every of two values: one thing if the situation is true, and one thing if it’s false:
let age = 18 let canVote = age >= 18 ? "Sure" : "No"
When that code runs,
canVote will probably be set to “Sure” as a result of
age is ready to 18.
for loops run some code for each merchandise in a set, or in a customized vary. For instance:
let platforms = ["iOS", "macOS", "tvOS", "watchOS"] for os in platforms print("Swift works on (os).")
You can even loop over a variety of numbers:
for i in 1...12 print("5 x (i) is (5 * i)")
1...12 comprises the values 1 by means of 12 inclusive. If you wish to exclude the ultimate quantity, use
..< as a substitute:
for i in 1..<13 print("5 x (i) is (5 * i)")
Tip: In the event you don’t want the loop variable, use
var lyric = "Haters gonna" for _ in 1...5 lyric += " hate" print(lyric)
There are additionally
whereas loops, which execute their loop physique till a situation is fake, like this:
var rely = 10 whereas rely > 0 print("(rely)…") rely -= 1 print("Go!")
You should utilize
proceed to skip the present loop iteration and proceed to the next one:
let information = ["me.jpg", "work.txt", "sophie.jpg"] for file in information if file.hasSuffix(".jpg") == false proceed print("Discovered image: (file)")
break to exit a loop and skip all remaining iterations.
To create a brand new perform, write
func adopted by your perform’s identify, then add parameters inside parentheses:
func printTimesTables(quantity: Int) for i in 1...12 print("(i) x (quantity) is (i * quantity)") printTimesTables(quantity: 5)
We have to write
quantity: 5 on the name web site, as a result of the parameter identify is a part of the perform name.
To return information from a perform, inform Swift what sort it’s, then use the
return key phrase to ship it again. For instance, this returns a cube roll:
func rollDice() -> Int return Int.random(in: 1...6) let end result = rollDice() print(end result)
In case your perform comprises solely a single line of code, you possibly can take away the
return key phrase:
func rollDice() -> Int Int.random(in: 1...6)
Returning a number of values from features
Tuples retailer a hard and fast variety of values of particular sorts, which is a handy solution to return a number of values from a perform:
func getUser() -> (firstName: String, lastName: String) (firstName: "Taylor", lastName: "Swift") let person = getUser() print("Title: (person.firstName) (person.lastName)")
In the event you don’t want all of the values from the tuple you possibly can destructure the tuple to tug it aside into particular person values, then
_ to inform Swift to disregard some:
let (firstName, _) = getUser() print("Title: (firstName)")
Customizing parameter labels
In the event you don’t need to go a parameter’s identify when calling a perform, place an underscore earlier than it:
func isUppercase(_ string: String) -> Bool string == string.uppercased() let string = "HELLO, WORLD" let end result = isUppercase(string)
An alternate is to write down a second identify earlier than the primary: one to make use of externally, and one internally:
func printTimesTables(for quantity: Int) for i in 1...12 print("(i) x (quantity) is (i * quantity)") printTimesTables(for: 5)
In that code
for is used externally, and
quantity is used internally.
Offering default values for parameters
We are able to present default parameter values by writing an equals after the kind then offering a price, like this:
func greet(_ individual: String, formal: Bool = false) if formal print("Welcome, (individual)!") else print("Hello, (individual)!")
Now we are able to name
greet() in two methods:
greet("Tim", formal: true) greet("Taylor")
Dealing with errors in features
To deal with errors in features it’s essential inform Swift which errors can occur, write a perform that may throw errors, then name it and deal with any issues.
First, outline the errors that may happen:
enum PasswordError: Error case brief, apparent
Subsequent, write a perform that may throw errors. That is finished by inserting
throws into the perform’s sort, then through the use of
throw to set off particular errors:
func checkPassword(_ password: String) throws -> String if password.rely < 5 throw PasswordError.brief if password == "12345" throw PasswordError.apparent if password.rely < 10 return "OK" else return "Good"
Now name the throwing perform by beginning a
do block, calling the perform utilizing
strive, then catching errors that happen:
let string = "12345" do let end result = strive checkPassword(string) print("Score: (end result)") catch PasswordError.apparent print("I've the identical mixture on my baggage!") catch print("There was an error.")
In the case of catching errors, you should at all times have a
catch block that may deal with each type of error.
You’ll be able to assign performance on to a relentless or variable like this:
let sayHello = print("Hello there!") sayHello()
In that code,
sayHello is a closure – a piece of code we are able to go round and name at any time when we wish. If you need the closure to simply accept parameters, they have to be written contained in the braces:
let sayHello = (identify: String) -> String in "Hello (identify)!"
in is used to mark the tip of the parameters and return sort – every thing after that’s the physique of the closure itself.
Closures are used extensively in Swift. For instance, there’s an array methodology referred to as
filter() that runs all components of the array by means of a take a look at, and any that return true for the take a look at get returned in a brand new array.
We are able to present that take a look at utilizing a closure, so we might filter an array to incorporate solely names that start with T:
let workforce = ["Gloria", "Suzanne", "Tiffany", "Tasha"] let onlyT = workforce.filter( (identify: String) -> Bool in return identify.hasPrefix("T") )
Contained in the closure we checklist the parameter
filter() passes us, which is a string from the array. We additionally say that our closure returns a Boolean, then mark the beginning of the closure’s code through the use of
in – after that, every thing else is regular perform code.
Trailing closures and shorthand syntax
Swift has a number of tips up its sleeve to make closures simpler to learn. Right here’s some code that filters an array to incorporate solely names starting with “T”:
let workforce = ["Gloria", "Suzanne", "Tiffany", "Tasha"] let onlyT = workforce.filter( (identify: String) -> Bool in return identify.hasPrefix("T") ) print(onlyT)
Instantly you possibly can see that the physique of the closure has only a single line of code, so we are able to take away
let onlyT = workforce.filter( (identify: String) -> Bool in identify.hasPrefix("T") )
filter() have to be given a perform that accepts one merchandise from its array, and returns true if it ought to be within the returned array.
As a result of the perform we go in should behave like that, we don’t have to specify the kinds in our closure. So, we are able to rewrite the code to this:
let onlyT = workforce.filter( identify in identify.hasPrefix("T") )
We are able to go additional utilizing particular syntax referred to as trailing closure syntax, which appears like this:
let onlyT = workforce.filter identify in identify.hasPrefix("T")
Lastly, Swift can present brief parameter names for us so we don’t even write
identify in any extra, and as a substitute depend on a specifically named worth offered for us:
let onlyT = workforce.filter $0.hasPrefix("T")
Structs allow us to create our personal customized information sorts, full with their very own properties and strategies:
struct Album let title: String let artist: String var isReleased = true func printSummary() print("(title) by (artist)") let purple = Album(title: "Pink", artist: "Taylor Swift") print(purple.title) purple.printSummary()
After we create situations of structs we achieve this utilizing an initializer – Swift lets us deal with our struct like a perform, passing in parameters for every of its properties. It silently generates this memberwise initializer based mostly on the struct’s properties.
If you wish to have a struct’s methodology change one in every of its properties, mark it as mutating:
mutating func removeFromSale() isReleased = false
A computed property calculates its worth each time it’s accessed. For instance, we might write an
Worker struct that tracks what number of days of trip remained for that worker:
struct Worker let identify: String var vacationAllocated = 14 var vacationTaken = 0 var vacationRemaining: Int vacationAllocated - vacationTaken
To have the ability to write to
vacationRemaining we have to present each a getter and a setter:
var vacationRemaining: Int get vacationAllocated - vacationTaken set vacationAllocated = vacationTaken + newValue
newValue is offered by Swift, and shops no matter worth the person was assigning to the property.
Property observers are items of code that run when properties change:
didSet runs when the property simply modified, and
willSet runs earlier than the property modified.
We might show
didSet by making a
Recreation struct print a message when the rating adjustments:
struct Recreation var rating = 0 didSet print("Rating is now (rating)") var recreation = Recreation() recreation.rating += 10 recreation.rating -= 3
Initializers are particular features that put together a brand new struct occasion for use, guaranteeing all properties have an preliminary worth.
Swift generates one based mostly on the struct’s properties, however you possibly can create your individual:
struct Participant let identify: String let quantity: Int init(identify: String) self.identify = identify quantity = Int.random(in: 1...99)
Necessary: Initializers don’t have
func earlier than them, and don’t explicitly return a price.
Swift has a number of choices for entry management inside structs, however 4 are the most typical:
non-publicfor “don’t let something exterior the struct use this.”
non-public(set)for “something exterior the struct can learn this, however don’t allow them to change it.”
fileprivatefor “don’t let something exterior the present file use this.”
publicfor “let anybody, anyplace use this.”
struct BankAccount non-public(set) var funds = 0 mutating func deposit(quantity: Int) funds += quantity mutating func withdraw(quantity: Int) -> Bool if funds > quantity funds -= quantity return true else return false
As a result of we used
funds from exterior the struct is okay however writing isn’t doable.
Static properties and strategies
Swift helps static properties and strategies, permitting you so as to add a property or methodology on to the struct itself fairly than to 1 occasion of the struct:
struct AppData static let model = "1.3 beta 2" static let settings = "settings.json"
Utilizing this method, in all places we have to examine or show one thing just like the app’s model quantity we are able to learn
Courses allow us to create customized information sorts, and are completely different from structs in 5 methods.
The primary distinction is that we are able to create courses by inheriting performance from different courses:
class Worker let hours: Int init(hours: Int) self.hours = hours func printSummary() print("I work (hours) hours a day.") class Developer: Worker func work() print("I am coding for (hours) hours.") let novall = Developer(hours: 8) novall.work() novall.printSummary()
If a toddler class needs to vary a way from a guardian class, it should use
override func printSummary() print("I spend (hours) hours a day looking Stack Overflow.")
The second distinction is that initializers are extra difficult with courses. There’s a number of complexity right here, however there are three key factors:
- Swift gained’t generate a memberwise initializer for courses.
- If a toddler class has customized initializers, it should at all times name the guardian’s initializer after it has completed organising its personal properties.
- If a subclass doesn’t have any initializers, it mechanically inherits the initializers of its guardian class.
class Car let isElectric: Bool init(isElectric: Bool) self.isElectric = isElectric class Automotive: Car let isConvertible: Bool init(isElectric: Bool, isConvertible: Bool) self.isConvertible = isConvertible tremendous.init(isElectric: isElectric)
tremendous permits us to name as much as strategies that belong to our guardian class, reminiscent of its initializer.
The third distinction is that every one copies of a category occasion share their information, which means that adjustments you make to 1 will mechanically change different copies.
class Singer var identify = "Adele" var singer1 = Singer() var singer2 = singer1 singer2.identify = "Justin" print(singer1.identify) print(singer2.identify)
That may print “Justin” for each – despite the fact that we solely modified one in every of them, the opposite additionally modified. As compared, struct copies don’t share their information.
The fourth distinction is that courses can have a deinitializer that will get referred to as when the final reference to an object is destroyed.
So, we might create a category that prints a message when it’s created and destroyed:
class Consumer let id: Int init(id: Int) self.id = id print("Consumer (id): I am alive!") deinit print("Consumer (id): I am useless!") for i in 1...3 let person = Consumer(id: i) print("Consumer (person.id): I am in management!")
The ultimate distinction is that courses allow us to change variable properties even when the category itself is fixed:
class Consumer var identify = "Paul" let person = Consumer() person.identify = "Taylor" print(person.identify)
Because of this, courses don’t want the
mutating key phrase with strategies that change their information.
Protocols outline performance we anticipate an information sort to help, and Swift ensures our code follows these guidelines.
For instance, we might outline a
Car protocol like this:
protocol Car func estimateTime(for distance: Int) -> Int func journey(distance: Int)
That lists the required strategies for this protocol to work, however doesn’t include any code – we’re specifying solely methodology names, parameters, and return sorts.
Upon getting a protocol you may make information sorts conform to it by implementing the required performance. For instance, we might make a
Automotive struct that conforms to
struct Automotive: Car func estimateTime(for distance: Int) -> Int distance / 50 func journey(distance: Int) print("I am driving (distance)km.")
All of the strategies listed in
Car should exist precisely in
Automotive, with the identical identify, parameters, and return sorts.
Now you possibly can write a perform that accepts any type of sort that conforms to
Car, as a result of Swift is aware of it implements each
func commute(distance: Int, utilizing car: Car) if car.estimateTime(for: distance) > 100 print("Too gradual!") else car.journey(distance: distance) let automobile = Automotive() commute(distance: 100, utilizing: automobile)
Protocols may require properties, so we might require properties for what number of seats autos have and what number of passengers they presently have:
protocol Car var identify: String get var currentPassengers: Int get set func estimateTime(for distance: Int) -> Int func journey(distance: Int)
That provides two properties: one marked with
get that may be a relentless or computed property, and one marked with
get set that may be a variable or a computed property with a getter and setter.
Now all conforming sorts should add implementations of these two properties, like this for
let identify = "Automotive" var currentPassengers = 1
Tip: You’ll be able to conform to as many protocols as you want, simply by itemizing them separated with a comma.
Extensions allow us to add performance to any sort. For instance, Swift’s strings have a way for trimming whitespace and new strains, but it surely’s fairly lengthy so we might flip it into an extension:
extension String func trimmed() -> String self.trimmingCharacters(in: .whitespacesAndNewlines) var quote = " The reality isn't pure and by no means easy " let trimmed = quote.trimmed()
If you wish to change a price instantly fairly than returning a brand new worth, mark your methodology as
mutating like this:
extension String mutating func trim() self = self.trimmed() quote.trim()
Extensions may add computed properties to sorts, like this one:
extension String var strains: [String] self.parts(separatedBy: .newlines)
parts(separatedBy:) methodology splits a string into an array of strings utilizing a boundary of our selecting, which on this case is new strains.
We are able to now use that property with all strings:
let lyrics = """ However I maintain cruising Cannot cease, will not cease shifting """ print(lyrics.strains.rely)
Protocol extensions prolong an entire protocol so as to add computed properties and methodology implementations, so any sorts conforming to that protocol get them.
Set all conform to the
Assortment protocol, so we are able to add a computed property to all three of them like this:
extension Assortment var isNotEmpty: Bool isEmpty == false
Now we are able to put it to make use of:
let company = ["Mario", "Luigi", "Peach"] if company.isNotEmpty print("Visitor rely: (company.rely)")
This method means we are able to checklist required strategies in a protocol, then add default implementations of these inside a protocol extension. All conforming sorts then get to make use of these default implementations, or present their very own as wanted.
Optionals symbolize the absence of knowledge – for instance, they distinguish between an integer having the worth 0, and having no worth in any respect.
To see optionals in motion, take into consideration this code:
let opposites = [ "Mario": "Wario", "Luigi": "Waluigi" ] let peachOpposite = opposites["Peach"]
That makes an attempt to learn the worth connected to the important thing “Peach”, which doesn’t exist, so this will’t be an everyday string. Swift’s resolution is named optionals, which implies information that may be current or may not.
An optionally available string may need a string ready inside for us, or there may be nothing in any respect – a particular worth referred to as
nil, which means “no worth”. Any type of information will be optionally available, together with
Bool, in addition to situations of enums, structs, and courses.
Swift gained’t allow us to use optionally available information instantly, as a result of it may be empty. Which means we have to unwrap the optionally available to make use of it – we have to look inside to see if there’s a price, and, if there’s, take it out and use it.
Swift provides us a number of methods of unwrapping optionals, however the one you’ll see most appears like this:
if let marioOpposite = opposites["Mario"] print("Mario's reverse is (marioOpposite)")
That reads the optionally available worth from the dictionary, and if it has a string inside it will get unwrapped – the string inside will get positioned into the
marioOpposite fixed, and isn’t optionally available any extra. As a result of we have been in a position to unwrap the optionally available, the situation is a hit so the
print() code is run.
Unwrapping optionals with guard
Swift has a second manner of unwrapping optionals, referred to as
guard let, which is similar to
if let besides it flips issues round:
if let runs the code inside its braces if the optionally available had a price, and
guard let runs the code if the optionally available didn’t have a price.
It appears like this:
func printSquare(of quantity: Int?) guard let quantity = quantity else print("Lacking enter") return print("(quantity) x (quantity) is (quantity * quantity)")
In the event you use
guard to examine a perform’s inputs are legitimate, Swift requires you to make use of
return if the examine fails. Nevertheless, if the optionally available you’re unwrapping has a price inside, you should utilize it after the
guard code finishes.
Tip: You should utilize guard with any situation, together with ones that don’t unwrap optionals.
Swift has a 3rd manner of unwrapping optionals, referred to as the nil coalescing operator – it unwraps an optionally available and gives a default worth if the optionally available was empty:
let tvShows = ["Archer", "Babylon 5", "Ted Lasso"] let favourite = tvShows.randomElement() ?? "None"
The nil coalescing operator is beneficial in lots of locations optionals are created. For instance, creating an integer from a string returns an optionally available
Int? as a result of the conversion may need failed. Right here we are able to use nil coalescing to offer a default worth:
let enter = "" let quantity = Int(enter) ?? 0 print(quantity)
Non-obligatory chaining reads optionals inside optionals, like this:
let names = ["Arya", "Bran", "Robb", "Sansa"] let chosen = names.randomElement()?.uppercased() print("Subsequent in line: (chosen ?? "Nobody")")
Non-obligatory chaining is there on line 2: a query mark adopted by extra code. It permits us to say “if the optionally available has a price inside, unwrap it then…” and add extra code. In our case we’re saying “if we received a random aspect from the array, uppercase it.”
When calling a perform that may throw errors, we are able to use
strive? to transform its end result into an optionally available containing a price on success, or
nil in any other case.
Right here’s the way it appears:
enum UserError: Error case badID, networkFailed func getUser(id: Int) throws -> String throw UserError.networkFailed if let person = strive? getUser(id: 23) print("Consumer: (person)")
getUser() perform will at all times throw
networkFailed, however we don’t care what was thrown – all we care about is whether or not the decision despatched again a person or not.
We’ve coated the vast majority of Swift language fundamentals right here, however actually we’ve solely scratched the floor of what the language does. Happily, with what you’ve discovered you already know sufficient to construct some unbelievable software program with Swift and SwiftUI.
In the event you’re eager to proceed your studying, I counsel you go to my 100 Days of SwiftUI course, which teaches you the best way to construct unbelievable apps for iOS utilizing Swift – it’s all free, and has tons of of movies too.