MuseumObject
type for your MetMuseum app. In this demo, I’m using an Xcode playground and writing in Swift. If you’re following along, start up Xcode and open the module1-lesson2 playground in the starter folder.
MuseumObject
struct that you’ll fill in and a showImage()
method stub.
MuseumObjectView
that showImage()
will use to display the art object’s image.
First, declare the properties your app will use:
let objectID: Int
let title: String
let objectURL: String
let primaryImageSmall: String
let creditLine: String
let isPublicDomain: Bool
MuseumObject
will have values for these properties. As you develop your app, you might need to add more properties, but these are enough for now.
MuseumObject
now has all the properties that MuseumObjectView
uses, so go ahead and uncomment it.
A struct definition is just a template for objects. It doesn’t do anything on its own. You have to instantiate an object — initialize it with parameter values
showImage()
.
So, instantiate two objects — one in the public domain and the other not in the public domain. Copy this code from the transcript below this video:
let object_pd =
MuseumObject(objectID: 436535,
title: "Wheat Field with Cypresses",
objectURL: "https://www.metmuseum.org/art/collection/search/436535",
primaryImageSmall: "https://images.metmuseum.org/CRDImages/ep/original/DT1567.jpg",
creditLine: "Purchase, The Annenberg Foundation Gift, 1993",
isPublicDomain: true)
let object =
MuseumObject(objectID: 13061,
title: "Cypress and Poppies",
objectURL: "https://www.metmuseum.org/art/collection/search/13061",
primaryImageSmall: "",
creditLine: "Gift of Iola Stetson Haverstick, 1982",
isPublicDomain: false)
MuseumObject
type, each representing different art objects. These instances are initialized with specific property values, which will be used when working with the data of these art objects.
MuseumObject
: All the properties are constant, and you’re not currently planning to implement any method that changes any values. Swift even defines the init
method for structs, so you don’t have to. If you typed all the previous code instead of copy-pasting, you’ve seen this in practice. If not, try it now — start typing a new object creation:
let obj = MuseumObject(
You get a suggested auto-completion with an argument for each property, in the same order that you declared them.
Delete this line.
MuseumObject
to a class. Do this now:
class MuseumObject {
...
}
The playground flags an error. If yours is thinking about it for too long, click run.
init()
method, so uncomment this code and add all your parameters:
init(objectID: Int,
title: String,
objectURL: String,
primaryImageSmall: String,
creditLine: String,
isPublicDomain: Bool) {
self.objectID = objectID
self.title = title
self.objectURL = objectURL
self.primaryImageSmall = primaryImageSmall
self.creditLine = creditLine
self.isPublicDomain = isPublicDomain
}
self
to differentiate the object’s properties from the parameter values. The arguments are in the same order as the property declarations, but that’s only so you don’t have to change the instantiations you already wrote.
showImage()
:
if isPublicDomain {
PlaygroundPage.current.setLiveView(MuseumObjectView(object: self))
} else {
guard let url = URL(string: objectURL) else { return }
PlaygroundPage.current.liveView = SFSafariViewController(url: url)
}
In a playground, you can either set the live view to a view, or to a view controller.
MuseumObjectView
uses AsyncImage(url:)
to download and display primaryImagesmall
. SFSafariViewController
, which you get by importing SafariServices
, loads objectURL
into an embedded Safari browser.
showImage()
. First, show the public domain image:
object_pd.showImage()
Run the playground. If nothing appears in your playground, stop it and run it again.
object_pd
is in the public domain, so showImage()
sets the playground’s live view to MuseumObjectView
.
object_pd.showImage()
and add this one:
object.showImage()
Run the playground again:
object
is not in the public domain, so showImage()
loads its web page in a Safari browser.
Structs and classes have other differences:
- You can create subclasses of a class. This is inheritance, and it’s a topic in the next lesson.
- Structs are value types; classes are reference types.
-
mutating
.
If a struct method modifies a struct object, you must mark it as
title
to be variable:
var title: String
MuseumObject
back to a struct:
struct MuseumObject {
object_pd
instantiation:
var object2 = object_pd
object2.title = "Sunflowers"
object.showImage()
and uncomment object_pd.showImage()
, then run the playground.
MuseumObject
is a struct, MuseumObjectView
still displays the title Wheat Field with Cypresses because object2
is a copy of object_pd
: Changing object2.title
doesn’t affect object_pd.title
.
MuseumObject
is a class?
class MuseumObject {
Run the playground again.
MuseumObject
is a class, MuseumObjectView
displays the title Sunflowers because object2
is the same object as object_pd
: Changing object2.title
changes object_pd.title
.
MuseumObject
is a class, changing object2
’s title
works even if you declare it as a constant class object:
let object2 = object_pd
because the constant value is its location in memory, not its contents.
Run the playground again to confirm this.
MuseumObject
:
func changeTitle(to newTitle: String) {
title = newTitle
}
object2.title = "Sunflowers"
to this:
object2.changeTitle(to: "Sunflowers")
MuseumObject
is a class, this works the same as before.
MuseumObject
back to a struct.
title = newTitle
:
Cannot assign to property: ‘self’ is immutable
changeTitle
as mutating
:
mutating func changeTitle(to newTitle: String) {
The error goes away. Why do you need to tell the compiler this method mutates the struct object?
object2
as a constant struct object — let object2 = object_pd
— then every property in that struct object is constant. You mark changeTitle(to:)
as mutating
so Swift knows that a constant struct object isn’t allowed to call it.
let
to var
, the error goes away.
MuseumObject
to a class again. You get an error on mutating
: ‘mutating’ is not valid on instance methods in classes. So mutating
really sets structs apart from classes.
MuseumObject
back to a struct. You want object2
to be a copy of object_pd
for this next line of code.
showImage()
lines:
object2 .showImage()
Run the playground:
object_pd
.
isPublicDomain
to be private
:
private let isPublicDomain: Bool
private
doesn’t prevent showImage()
from using it. But try typing this outside the struct:
if object.isPublicDomain { }
isPublicDomain
doesn’t show up in the auto-completion suggestions. There’s also an error message (click the run button if you don’t see it):
‘isPublicDomain’ is inaccessible due to ‘private’ protection level
if ...
line and run the playground: There’s no problem passing a value to isPublicDomain
in the initializer.
init
method and run the playground — there’s an error message!
‘MuseumObject’ initializer is inaccessible due to ‘private’ protection level
private
property but if you write your own, it’s OK.
That ends this demo. Continue with the lesson for a summary.