Some variation between this lesson and the previous one 😉
Section 1 like in the previous lesson, we start in the MotionDetector class. Everything is the same with regard to the private properties and the initializer. Discrepancies creep in at the start() function on line 24. What they did was switch UIDevice.current.beginGeneratingDeviceOrientationNotifications() and starting the orientationObserver with checking motionManager.isDeviceMotionAvailable and starting our Timer. Our three @Published properties are given values and observed when updateMotionData() is fired, with the closure onUpdate() being run at the end. Roll & Pitch are updated using our custom extension at the bottom of the file, and zAcceleration is updated from the sub-property of the property of our data constant. The stop() function has the motionManager cease all activity, ends our timer from running, and switches off our orientationObserver notification. The closure deinit runs our stop() function when the system is ready to dismiss our motionDetector class. If we do not do this, our device will continue to monitor motion data, using up system resources and wasting battery.
Section 2 our NeedleSeismometer measures vibrations using a needle. It focuses on the zAcceleration property of our motionDetector in response to the device’s movement. The MotionDetector object is in play as our EnvironmentObject. The three properties that follow are the needleAnchor, the amplification constant, and rotationAngle. Within our body, we have a VStack that embeds a ZStack. Our ZStack layers the content bottom to top, so our GaugeBackground is laying flat like a dinner plate. The rectangle is actually our needle- thin (width of 5) and it wobbles as our device moves. The rectangle has an overlay view modifier, which carries a VStack, inside of the stack we have a Spacer pushing everything after to the bottom. Finally we have a Circle meant to ‘pin’ our needle to the bottom as an anchor while it moves. The real anchor is our needlePoint property, it tells our Rectangle to stay at one point (UnitPoint needleAnchor).
Section 3 we examine the GraphSeismometer view. At the top, we have our MotionDetector, the EnvironmentObject. Below this, we have our State private variable array of Double, named data. It is initialized to be an empty array. Under this, we have a constant value maxData which is set to 1000. The constant maxData is meant to be a cap for length of the array data, where once we have 1001 entries, it will remove the first entry in the array. Then we have the private State variable sensitivity. The more ‘sensitive’ (precise) this variable, the more detailed with higher and lower reactions to movement of the device. graphMaxValue is a computed property, this takes our three previous properties: sensitivity, graphMaxValueMostSensitive, graphMaxValueLeastSensitive and does math to figure out how our graph will be drawn on the device. Below this, graphMinValue is the negative value of graphMaxValue.
LineGraph is the custom View that reflects our device’s movement & movement history. I see it and think of those cop shows with the polygraph test 👮♂️. Its parameters are the data, maxData, (remember, this keeps track of how many entries our array is supposed to store), minValue and maxValue. Beneath this view, we have five modifiers: clipped, background, cornerRadius, padding & aspectRatio. AspectRatio is funny- when we change the value to greater than 1, we get a wide (horizontal) rectangle. Change the value to a decimal between 0 and 1, we get a thin (vertical) rectangle. Under this view we have our Slider, which leverages our sensitivity @State variable to adjust how our LineGraph details movement. Under the Text, we have our onAppear view modifier, and inside this closure, we are observing responses from the detector onUpdate closure.
Section 4 we dive into the SeismometerBrowser, the base View of our app. From here we navigate to NeedleSeismometer or GraphSeismometer, both embedded in a List, which itself is embedded in a NavigationSplitView. To direct our users to the actual Needle or Graph, we have NavigationLink, which behaves as a Button. Inside the link, we are drawing our view with the HStack, VStack, Image & Text components. At the top we have the @StateObject detector, which is our MotionDetector that will be passed down to our subviews Needle & Graph. Below we are calling the motion detector object in the .environmentObject modifier. Beneath that, we have .onAppear() & .onDisappear, which toggle starting and stopping for the motion detector.
For Section 5, just as in the previous lesson, we have the Double extension. This is here so we have a reusable function that formats the digits we show our users.
Find the conformance to the Observable Macro here











