<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Mark Edward Murray on Medium]]></title>
        <description><![CDATA[Stories by Mark Edward Murray on Medium]]></description>
        <link>https://medium.com/@MarkEdwardMurray?source=rss-1b005c721fda------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*9bMEKI68cF6-hfzlC5cUlg.jpeg</url>
            <title>Stories by Mark Edward Murray on Medium</title>
            <link>https://medium.com/@MarkEdwardMurray?source=rss-1b005c721fda------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 22 May 2026 13:51:06 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@MarkEdwardMurray/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Supporting Face ID on the iPhone X]]></title>
            <link>https://medium.com/@MarkEdwardMurray/supporting-face-id-on-the-iphone-x-a02fd34903b8?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/a02fd34903b8</guid>
            <category><![CDATA[facial-recognition]]></category>
            <category><![CDATA[biometrics]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[iphone-x]]></category>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Fri, 03 Nov 2017 14:27:24 GMT</pubDate>
            <atom:updated>2019-02-20T13:19:56.520Z</atom:updated>
            <content:encoded><![CDATA[<p>Helping people do what’s best with their money requires providing them with responsible security measures to protect their private financial data. In Betterment’s mobile apps, this means including trustworthy but convenient local authentication options for resuming active login sessions. Three years ago, in 2014, we implemented Touch ID support as an alternative to using PIN entry in our iOS app. Today, on its first day, we’re thrilled to announce that the Betterment iOS app fully supports Apple’s new Face ID technology on the iPhone X.</p><h4>Trusting the Secure Enclave</h4><p>While we’re certainly proud of shipping this feature quickly, a lot of credit is due to Apple for how seriously the company takes device security and data privacy as a whole. The hardware feature of the Secure Enclave included on iPhones since the 5S make for a readily <a href="https://www.apple.com/business/docs/iOS_Security_Guide.pdf">trustworthy connection to the device and its operating system</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*B1J35a2gGYs-3oUX0pV8bg.png" /></figure><p>From an application’s perspective, this relationship between a biometric scanner and the Secure Enclave is simplified to a boolean response. When requested through the Local Authentication framework, the biometry evaluation either succeeds or fails separate from any given state of an application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*vIy8HoN7JhLWT-q8CcwXow.png" /></figure><p>The “reply” completion closure of evaluatePolicy(_:localizedReason:reply:)</p><p>This made testing from the iOS Simulator a viable option for gaining a reasonable degree of certainty that our application would behave as expected when running on a device, thus allowing us to prepare a build in advance of having a device to test on.</p><h4>LABiometryType</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*1iWIyJGb5RsdnsJmBMOmEg.png" /></figure><p>Since we’ve been <a href="https://www.synopsys.com/blogs/software-security/integrating-touch-id-into-ios-applications/">securely using Touch ID for years</a>, adapting our existing implementation to include Face ID was a relatively minor change. Thanks primarily to the simple addition of the LABiometryType enum newly available in iOS 11, it’s easy for our application to determine which biometry feature, if any, is available on a given device. This is such a minor change, in fact, that we were able to reuse all of our same view controllers that we had built for Touch ID with only a handful of string values that are now determined at runtime.</p><p>One challenge we have that most existing iOS apps share is the need to still support older iOS versions. For this reason, we chose to wrap LABiometryTypebehind our own BiometryType enum. This allows us to encapsulate both the need to use an iOS 11 compiler flag and the need to call canEvaluatePolicy(_:error:) on an instance of LAContext before accessing its biometryType property into a single calculated property:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/96b427f9d2d6d87e799b32b147ce09fc/href">https://medium.com/media/96b427f9d2d6d87e799b32b147ce09fc/href</a></iframe><h4>NSFaceIDUsageDescription</h4><p>The other difference with Face ID is the new NSFaceIDUsageDescriptionprivacy string that should be included in the application’s Info.plist file. This is a departure from Touch ID which does not require a separate privacy permission, and which uses the localizedReason string parameter when showing its evaluation prompt.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*vnRAcpxUDraeuKIrMF7qbQ.png" /></figure><p>Touch ID evaluation prompt displaying the localized reason</p><p>While Face ID does not seem to make a use of that localizedReason string during evaluation, without the privacy string the iPhone X will run the application’s Local Authentication feature in compatibility mode. This informs the user that the application should work with Face ID but may do so imperfectly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*9yP2KL5QfLtEFs1xlo-3mw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*GNLjonmW9mb9kjDyShMOfg.png" /></figure><p>Face ID permissions prompt without (left) and with (right) an NSFaceIDUsageDescription string included in the Info.plist</p><p>This compatibility mode prompt is undesirable enough on its own, but it also clued us into the need to check for potential security concerns opened up by this forwards-compatibility-by-default from Apple.</p><p>Thankfully, the changes to the Local Authentication framework were done in such a way that we determined there wasn’t a security risk, but it did leave a problematic user experience in reaching a potentially-inescapable screen when selecting “Don’t Allow” on the privacy permission prompt. Since we believe strongly in our users’ right to say “no”, resolving this design issue was the primary reason we prioritized shipping this update.</p><h4>Ship It</h4><p>If your mobile iOS app also displays sensitive information and uses Touch ID for biometry-based local authentication, join us in making the easy adaption to delight your users with full support for Face ID on the iPhone X.</p><p><em>Originally published at </em><a href="https://www.betterment.com/resources/supporting-apple-ios-face-id/"><em>www.betterment.com</em></a><em> on November 3, 2017.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a02fd34903b8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to Specta/Expecta]]></title>
            <link>https://medium.com/@MarkEdwardMurray/introduction-to-specta-expecta-edb20c331226?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/edb20c331226</guid>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Fri, 17 Apr 2015 14:17:12 GMT</pubDate>
            <atom:updated>2015-04-21T13:58:02.936Z</atom:updated>
            <content:encoded><![CDATA[<p>This week I dove into writing my own unit tests from scratch. While it’s true that I’ve been exposed to them consistently over the previous ten weeks of Flatiron’s program, I hadn’t yet attempted to manufacture my testing suite out of empty files. I did a number of things wrong in the process so here’s a quick guide with the intent of keeping others from getting stuck on some of those same things.</p><p>Before I get to showing code samples, let’s go over the set up. First, open your Podfile and add the ‘Specta’ and ‘Expecta’ pods to the ‘&lt;fileName&gt;Tests’ target and run ‘pod install’ in your terminal.</p><pre>// Podfile</pre><pre>Target ‘FISCardStreams’ do<br>end</pre><pre>Target ‘FISCardStreamsTests’ do<br>    pod ‘Specta’<br>    pod ‘Expecta’</pre><pre>end</pre><p>Make sure you now continue working in the *.xcworkspace file.</p><p>Next, in XCode, with the Alcatraz Package Manager already installed, add the Specta Templates package by Luiza-Cicone. This will generate our *Spec.m files with the correct targets and some boilerplate code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/538/1*AcJyb6uyVdBaLdRMnChGFg.png" /></figure><p>Now we’re ready to generate the test file. In our case, this is going to be a Spec for the FISComment custom class, so let’s create our FISCommentSpec file using the template. Create a New File with the following steps:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/1*gbksfL1wFRTjogJl2ofmYw.png" /><figcaption>Select the Specta template added by the Specta-Templates-Xcode package. Select “Next”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/718/1*syXdkm8C61_EhuXotrlemw.png" /><figcaption>Enter the class you wish to test. Select “Next”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/703/1*uD5tY96wd_u6P3RBGI6R3w.png" /><figcaption>(default) Select ONLY the &lt;filename&gt;Tests target at the bottom of the next window. Select “Create”.</figcaption></figure><p>This new FISCommentSpec.m file load with the following boilerplate code:</p><pre>#import “Specta.h”<br>#import “FISComment.h”</pre><pre>SpecBegin(FISComment)</pre><pre>describe(@”FISComment”, ^{<br> <br> beforeAll(^{</pre><pre>});<br> <br> beforeEach(^{</pre><pre>});<br> <br> it(@””, ^{</pre><pre>}); <br> <br> afterEach(^{</pre><pre>});<br> <br> afterAll(^{</pre><pre>});<br>});</pre><pre>SpecEnd</pre><p>Unfortunately, the headers aren’t set for using Expecta yet. We need to import it and define the shorthand:</p><pre>#import &lt;Foundation/Foundation.h&gt;<br>#import &lt;Specta/Specta.h&gt;<br>#define EXP_SHORTHAND<br>#import “Expecta.h”</pre><pre>#import “FISComment.h”</pre><p>The “SpecBegin(&lt;className&gt;)” and “SpecEnd” are wrappers similar to the “@implementation” and “@end” in your class files and the compiler will complain if these are interrupted or missing.</p><p>The “it(NSString *name, ^{ … });” blocks are the actual test containers with a string that describes specifically what the test does.</p><p>The “describe(NSString *name, ^{ … });” blocks are wrappers that prepend a string to the displayed name of all the tests (or “it” blocks) within it. These “name” strings are very useful in self-documenting what each test is intended to check for and what its scope is.</p><p>The “beforeAll(^{ … });”, “beforeEach(^{ … });”, “afterAll(^{ … });”, and “afterEach(^{ … });” blocks are for setting up (or resetting) the instance variables to test by and the instances of the test class to run tests on. Keep in mind that “beforeAll” and “afterAll” only get dispatched once for all of the tests that they contain, but “beforeEach” and “afterEach” get newly run for every “it” test block.</p><p>Since one of the great features of Expecta is its asynchronous nature, it’s generally advisable to use “beforeEach” in order to start each test fresh with the same data since “beforeAll” only gets called once. The “afterAll” and “afterEach” blocks aren’t typically needed unless resetting some outside class or user default. We won’t be using them here.</p><p>True to Test-Driven-Development (TDD) form, let’s start by writing some tests for FISCommentSpec. Let’s expect to write a designated initializer that sets all of the properties it needs, and a default initializer that calls the designated initializer to pass in non-nil objects.</p><p>Let’s declare the properties we want FISComment to have, and the instance names that we want to generate:</p><pre>SpecBegin(FISComment)<br>describe(@&quot;FISComment&quot;, ^{</pre><pre>    __block NSString *commentID;<br>    __block NSString *parentID;<br>    __block NSDate   *createdAt;<br>    __block NSString *content;</pre><pre>    __block FISComment *defaultComment;<br>    __block FISComment *designatedComment;</pre><pre>});<br>SpecEnd</pre><p>You’ll notice that we need to preface these declarations with “__block” (Note: this is a double-underscore). This places all of these objects within the scope of every test block in the current file. Now let’s set them in “beforeEach”:</p><pre>SpecBegin(FISComment)<br>describe(@&quot;FISComment&quot;, ^{</pre><pre>    { ... }</pre><pre>    beforeEach(^{</pre><pre>        commentID = @”commentID”;<br>        parentID = @”parentID”;<br>        createdAt = [NSDate date];<br>        content = @”content”;</pre><pre>        defaultComment = [[FISComment alloc]init];<br>        designatedComment = <br>            [[FISComment alloc] initWithCommentID:commentID];<br>    });<br>});<br>SpecEnd</pre><p>And now we can next some describe blocks for the methods we’re going to write. Make sure you close out the “beforeEach” block before continuing:</p><pre>SpecBegin(FISComment)<br>describe(@&quot;FISComment&quot;, ^{</pre><pre>    { ... }</pre><pre>    beforeEach(^{</pre><pre>        { ... }<br>    });</pre><pre>    describe(@&quot;default initializer&quot;, ^{<br>    });</pre><pre>    describe(@&quot;designated initializer&quot;, ^{<br>    });<br>});<br>SpecEnd</pre><p>Now let’s add some tests (“it” blocks) and test conditions:</p><pre>SpecBegin(FISComment)<br>describe(@&quot;FISComment&quot;, ^{</pre><pre>{ ... }</pre><pre>    beforeEach(^{</pre><pre>        { ... }<br>    });</pre><pre>    describe(@&quot;default initializer&quot;, ^{<br>        it(@&quot;sets all properties to default values&quot;, ^{<br>            expect(defaultComment.commentID).to.equal(@&quot;&quot;);<br>            expect(defaultComment.parentID ).to.equal(@&quot;&quot;);<br>            expect(defaultComment.createdAt).to.<br>                                       beKindOf([NSDate class]);<br>            expect(defaultComment.content  ).to.equal(@&quot;&quot;);  <br>        });<br>    });</pre><pre>    describe(@&quot;designated initializer&quot;, ^{<br>        it(@&quot;sets all properties to submitted values&quot;, ^{<br>           expect(designatedComment.commentID).to.equal(commentID);<br>           expect(designatedComment.parentID ).to.equal(parentID);<br>           expect(designatedComment.createdAt).to.equal(createdAt);<br>           expect(designatedComment.content  ).to.equal(content);<br>        });<br>    });<br>});<br>SpecEnd</pre><p>The readability of the condition lines is much of makes Expecta so great. Begin the line with the “expect(…)” which encapsulated the instance variable you are testing — in our case, each of the properties of a FISComment object. The “.to” is actually not required for the positive, but is a helpful syntactic tool that also acts a placeholder for the inversion specifier written as either “.toNot” or “.notTo”.</p><p>The final element is the match specifier — how the instance variables are being compared — which encapsulates the instance variable we are testing. In this example we are using “.equal(…)” and “.beKindOf(…)”, but there is an extensive list of built-in matchers detailed in <a href="https://github.com/specta/expecta">Expecta’s documentation</a>. Expecta is written to allow custom matchers to be written quickly, but in most cases, the built-in matchers can be applied.</p><p>At this point, we should run our tests to check that they fail. This important to make sure that you have not written tests that always pass and are thereby useless. This is the “Red” step in the <em>Red-Green-Refactor</em> TDD mantra.</p><p>Now let’s pursue those green lights by writing out our FISComment class to match our test’s expectations. We need it to have four properties, a designated initializer, and an overridden default initializer:</p><pre>FISComment.h</pre><pre>@interface FISComment : NSObject</pre><pre>@property (strong, nonatomic) NSString *commentID;<br>@property (strong, nonatomic) NSString *parentID;<br>@property (strong, nonatomic) NSDate   *createdAt;<br>@property (strong, nonatomic) NSString *content;</pre><pre>- (instancetype)init;</pre><pre>- (instancetype)initWithCommentID:(NSString *)commentID<br>                         parentID:(NSString *)parentID<br>                        createdAt:(NSDate   *)createdAt<br>                          content:(NSString *)content;</pre><pre>@end</pre><pre><br>FISComment.m</pre><pre>#import “FISComment.h”</pre><pre>@implementation FISComment</pre><pre>- (instancetype)init {<br>    self = [self initWithCommentID:@””<br>                          parentID:@””<br>                         createdAt:[NSDate date]<br>                           content:@””];<br>    return self;<br>}</pre><pre>- (instancetype)initWithCommentID:(NSString *)commentID<br>                         parentID:(NSString *)parentID<br>                        createdAt:(NSDate   *)createdAt<br>                          content:(NSString *)content {<br>    self = [super init];<br>    if (self) {<br>        _commentID = commentID;<br>        _parentID  = parentID;<br>        _createdAt = createdAt;<br>        _content   = content;<br>    }<br>    return self;<br>}</pre><pre>@end</pre><p>Now we can run our tests and see if they pass. If they do, we’ll see a list of green lights in the navigation window.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*eVWAE3LevpT4kNg68z_XYw.png" /></figure><p>This is concludes the “Green” phase. Now that we have the ability to check whether changes to our code break our FISComment class methods, we can graduate to the “Refactor” phase. In our case, we’ve already written two very solid initializers so we don’t have anything to refactor in FISComment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=edb20c331226" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Objective-C Class Initializers (a.k.a. Constructors)]]></title>
            <link>https://medium.com/@MarkEdwardMurray/objective-c-class-initializers-a-k-a-constructors-94dcd6d2ffd7?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/94dcd6d2ffd7</guid>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Thu, 02 Apr 2015 14:00:29 GMT</pubDate>
            <atom:updated>2015-04-02T14:50:26.848Z</atom:updated>
            <content:encoded><![CDATA[<p>Writing custom initializers and constructors for your data models is very useful for managing version control and API response updates. Their value is that by collecting everything needed to create an instance of your data model, not only can you create a complex object with a single method, but also localize all of your changes into one place. For such things as staying current with API responses that evolve over time, this provides a very easy means of updating your custom data model objects to the new expected JSON response object.</p><p>Class initializers have a variety of uses beyond translating JSON dictionaries to Objective-C data models. In addition to being particularly useful in Core Data applications, these common methods are class initializers that you may have called without realizing what they are:</p><pre>[NSString stringByAppendingString:(NSString*)string];<br>[NSNumber numberWithInteger:(NSInteger)integer];<br>[NSArray arrayWithArray:(NSArray *)array];<br>[NSDate dateWithTimeIntervalSince1970];<br>[NSCharacterSet letterCharacterSet];</pre><p>Since they’re so useful, let’s write one for the FISClass data model class.</p><p>Since custom initializers are foundational to writing class initializers, we’ll have to start by writing these. Here’s a quick refresher on doing this correctly, but a more thorough tutorial can be found on the <a href="http://rypress.com/tutorials/objective-c/classes">RyPress</a> website.</p><p>In the class’s interface section, declare all of the properties that the class needs to contain.</p><pre>FISClass.h</pre><pre>#import &lt;Foundation/Foundation.h&gt;</pre><pre>@class FISPerson;</pre><pre>@interface FISClass : NSObject</pre><pre>@property (strong, nonatomic) NSString *name;<br>@property (strong, nonatomic) NSNumber *roomNumber;<br>@property (strong, nonatomic) FISPerson *instructor;<br>@property (strong, nonatomic) NSArray *students;</pre><pre>@end</pre><p>Then declare the custom initializer that includes an argument for each property. This will become the “designated initializer.” If the designated initializer needs to be private, then leave it out of the header file.</p><pre>FISClass.h</pre><pre>#import &lt;Foundation/Foundation.h&gt;</pre><pre>@class FISPerson;</pre><pre>@interface FISClass : NSObject</pre><pre>@property (strong, nonatomic) NSString *name;<br>@property (strong, nonatomic) NSNumber *roomNumber;<br>@property (strong, nonatomic) FISPerson *instructor;<br>@property (strong, nonatomic) NSArray *students;</pre><pre>- (instancetype)initWithName:(NSString *)name<br>                  roomNumber:(NSNumber *)roomNumber<br>                  instructor:(FISPerson *)instructor<br>                    students:(NSArray *)students;</pre><pre>@end</pre><p>This method name will now autofill in your main (*.m) file. The method body should look like this:</p><pre>FISClass.m</pre><pre>#import &quot;FISClass.h&quot;<br>#import &quot;FISPerson.h&quot;</pre><pre>@implementation FISClass</pre><pre>- (instancetype)initWithName:(NSString *)name<br>                  roomNumber:(NSNumber *)roomNumber<br>                  instructor:(FISPerson *)instructor<br>                    students:(NSArray *)students {<br>    self = [super init];<br>    if (self) {<br>        _name = name;<br>        _roomNumber = roomNumber;<br>        _instructor = instructor;<br>        _students = students;<br>    }<br>    return self;<br>}</pre><p>You can then override the default initializer and even create a convenience initializer in the following manner:</p><pre>- (instancetype)init {<br>    self = [self initWithName:@&quot;&quot;<br>                   roomNumber:@0<br>                   instructor:[[FISPerson alloc]init]<br>                     students:@[]  ];<br>    return self;<br>}</pre><pre>- (instancetype)initWithName:(NSString *)name<br>                  roomNumber:(NSNumber *)roomNumber {<br>    self = [self initWithName:name<br>                   roomNumber:roomNumber<br>                   instructor:[[FISPerson alloc]init]<br>                     students:@[]  ];<br>    return self;<br>}</pre><p>These call the designated initializer with default objects so that they are not initialized as “nil,” thus allowing more clarity later during the debugging process. You will notice that “[super init]” is called only in the designated initializer. If the default initializer is overridden, it is good practice to note this publicly by adding it to the header filer. This has no effect upon performance, but it is a nice indicator that to other programmers and your “future self” that changes have been made to the implicit code.</p><p>Before we move on, let’s set up the FISPerson class with initializers, too.</p><pre>FISPerson.h</pre><pre>#import &lt;Foundation/Foundation.h&gt;</pre><pre>@interface FISPerson : NSObject</pre><pre>@property (strong, nonatomic) NSString *name;</pre><pre>- (instancetype)initWithName:(NSString *)name;</pre><pre>@end</pre><pre><br>FISPerson.m</pre><pre>#import &quot;FISPerson.h&quot;</pre><pre>@implementation FISPerson</pre><pre>- (instancetype)init {<br>    self = [self initWithName:@&quot;&quot;];<br>    return self;<br>}</pre><pre>- (instancetype)initWithName:(NSString *)name {<br>    self = [super init];<br>    if (self) {<br>        _name = name;<br>    }<br>    return self;<br>}</pre><pre>@end</pre><p>OK, great. Now calling these designated initializers would then look like this:</p><pre>FISPerson *zachDrossman = <br>    [[FISPerson alloc]initWithName:@&quot;Zach Drossman&quot;];<br>FISPerson *markMurray = <br>    [[FISPerson alloc]initWithName:@&quot;Mark Murray&quot;];<br>FISPerson *anishKumar =<br>    [[FISPerson alloc]initWithName:@&quot;Anish Kumar&quot;];</pre><pre>FISClass *ios004 = [[FISClass alloc]initWithName:@&quot;ios004&quot;<br>                                      roomNumber:@4<br>                                      instructor:zachDrossman<br>                                        students:@[markMurray,<br>                                                   anishKumar]  ];</pre><p>This ensures that someone unfamiliar with the custom classes will initialize all of the properties to some value and avoid the black abyss of “nil”.</p><p>Furthermore, if we already have a dictionary object containing the information for an instance of our FISClass object, then we can use a class initializer to pass the information to the class object, which then returns an instance of itself with the correct properties. The dictionary, for our purposes, should be formatted like this:</p><pre>NSDictionary *ios004Dictionary = <br>                @{ @&quot;name&quot; : @&quot;ios004&quot;,<br>                   @&quot;roomNumber&quot; : @4,<br>                   @&quot;instructor&quot; : @{ @&quot;name&quot; : @&quot;Zach Drossman&quot; },<br>                   @&quot;students&quot; : @[@{ @&quot;name&quot; : @&quot;Mark Murray&quot; },<br>                                   @{ @&quot;name&quot; : @&quot;Anish Kumar&quot; }  ]       <br>                 };</pre><p>Since this dictionary is nested with dictionaries for creating FISPerson objects that the FISClass object will contain, let’s start small by writing the FISPerson constructor first:</p><pre>FISPerson.h</pre><pre>@interface FISPerson : NSObject</pre><pre>{...}</pre><pre>+ (FISPerson *)createFromDictionary:<br>                                  (NSDictionary *)personDictionary;<br>@end</pre><pre><br>FISPerson.m</pre><pre>@implementation FISPerson</pre><pre>{...}</pre><pre>+ (FISPerson *)createFromDictionary:<br>                                 (NSDictionary *)personDictionary {</pre><pre>    FISPerson *newPerson = [[FISPerson alloc]<br>                         initWithName:personDictionary[@&quot;name&quot;] ];<br>    return newPerson;<br>}</pre><pre>@end</pre><p>Excellent! We’ll need to call this method when creating the FISClass object. You can see it applied in creating the “instructor” object and the “student” objects in the “students” array:</p><pre>FISClass.h</pre><pre>@interface FISClass : NSObject</pre><pre>{...}</pre><pre>+ (FISClass *)createFromDictionary:(NSDictionary *)classDictionary;</pre><pre>@end<br></pre><pre>FISClass.m</pre><pre>@implementation FISClass</pre><pre>{...}</pre><pre>+ (FISClass *)createFromDictionary:(NSDictionary *)classDictionary <br>{<br>    FISPerson *instructor = [FISPerson <br>               createFromDictionary:classDictionary[@&quot;instructor&quot;];</pre><pre>    NSMutableArray *mStudents = [[NSMutableArray alloc]init];<br>    for (NSDictionary *studentDictionary in <br>                                   classDictionary[@&quot;students&quot;]) {<br>        FISPerson *student = [FISPerson<br>                           createFromDictionary:studentDictionary];<br>        [mStudents addObject:student];<br>    }<br>    NSArray *students = [NSArray arrayWitharray:mStudents];</pre><pre>    FISClass *newClass = <br>        [FISClass initWithName:classDictionary[@&quot;name&quot;]<br>                    roomNumber:classDictionary[@&quot;roomNumber&quot;]<br>                    instructor:instructor<br>                      students:students];</pre><pre>    return newClass;<br>}</pre><pre>@end</pre><p>This class initializer can be called from elsewhere in your code, such as in a data manager or an API client’s internet request completion block. Simply pass the response object to the class initializer and it will create your data model instances on its own.</p><pre>NSMutableArray *classes = [[NSMutableArray alloc]init];</pre><pre>[FISIronboardAPIClient <br>           getClassesWithCompletion:^(NSArray *classDictionaries) {<br>    for (NSDictionary *classDictionary in classDictionaries) {<br>        [classes addObject:[FISClass <br>                            createFromDictionary:classDictionary]];<br>    }<br>    completionBlock(YES);<br>}];</pre><p>If, for example, the Ironboard API either changes the name of a dictionary element in the JSON response (such as altering “name” to “fullName”), or adding a new property to “person” objects such as a “studentID”, we can adapt to these changes quickly by altering the data model itself to the following:</p><pre>FISPerson.h</pre><pre>#import &lt;Foundation/Foundation.h&gt;</pre><pre>@interface FISPerson : NSObject</pre><pre>@property (strong, nonatomic) NSString *fullName;<br>@property (strong, nonatomic) NSString *studentID;</pre><pre>- (instancetype)init;</pre><pre>- (instancetype)initWithFullName:(NSString *)fullName<br>                       studentID:(NSString *)studentID;</pre><pre>+ (FISPerson *)createFromDictionary:<br>                                  (NSDictionary *)personDictionary;<br>@end<br></pre><pre>FISPerson.m</pre><pre>#import &quot;FISPerson.h&quot;</pre><pre>@implementation FISPerson</pre><pre>- (instancetype)init {<br>    self = [self initWithFullName:@&quot;&quot;<br>                        studentID:@&quot;&quot;];<br>    return self;<br>}</pre><pre>- (instancetype)initWithFullName:(NSString *)fullName <br>                       studentID:(NSString *)studentID {<br>    self = [super init];<br>    if (self) {<br>        _fullName = fullName;<br>        _studentID = studentID;<br>    }<br>    return self;<br>}</pre><pre>+ (FISPerson *)createFromDictionary:<br>                                 (NSDictionary *)personDictionary {<br>    FISPerson *newPerson = [[FISPerson alloc]<br>               initWithFullName:personDictionary[@&quot;fullName&quot;]<br>                      studentID:personDictionary[@&quot;studentID&quot;]   ];<br>    return newPerson;<br>}</pre><pre>@end</pre><p>And to do this compliance update we don’t even have to touch the FISClass or FISIronboardAPIClient files at all. This is the benefit to using class initializers for this purpose.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=94dcd6d2ffd7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Time]]></title>
            <link>https://medium.com/@MarkEdwardMurray/time-70270becaa2?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/70270becaa2</guid>
            <category><![CDATA[xkcd]]></category>
            <category><![CDATA[webcomics]]></category>
            <category><![CDATA[time]]></category>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Wed, 18 Mar 2015 15:02:54 GMT</pubDate>
            <atom:updated>2015-03-18T17:09:25.619Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Time, Love —<br>Time, Love —<br>Time, Love —<br>It’s only a change of time. — <a href="http://www.joshritter.com/so-runs-the-world-away/">Josh Ritter</a>, “<a href="https://soundcloud.com/jjc-1/josh-ritter-change-of-time">Change of Time</a>”</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/553/1*vrvmg7d9ULSQQZRjPAA8iw.png" /><figcaption><a href="http://xkcd.com/1190/">XKCD #1190</a> opening frame. View it as an <a href="http://vignette1.wikia.nocookie.net/xkcd-time/images/a/a9/Time-animated.gif/revision/latest?cb=20130627164320">animated gif</a>, or frame-by-frame with GeekWagon’s “<a href="http://geekwagon.net/projects/xkcd1190/">Time — at your own pace</a>” viewer.</figcaption></figure><p><a href="http://www.i-programmer.info/news/200-art/5705-the-enigma-of-xkcd-1190-.html">Happening</a> upon this cultural gem while researching another blog post, “Time” captured my attention and left me engrossed for hours. While I’ve been a casual fan of <a href="http://en.wikipedia.org/wiki/Randall_Munroe">Randall Munroe</a>’s web comic “XKCD” since I discovered it in college shortly after it launched, I had not discovered the profundity of post #1190 until just last night — and yes, it easily consumed over two hours of my evening.</p><blockquote>“And as Time unfolded, readers gradually figured out that it was a story,…”<br>- Randall Munroe, <a href="http://blog.xkcd.com/2013/07/29/1190-time/">xkcd blog</a></blockquote><p>It consists of 3,099 individual frames which were updated hourly between March 25 — July 26, 2013. This turned the slideshow into a long-form moving picture, blurring the line between comic and animation. While the original post now permanently displays the final image, thankfully there were a few devoted XKCD fans who archived the entire collection as it was released.</p><p>These devotees were the first of an entire subculture among XKCD fans which spawned during the four-month course of #1190. These fans left the internet with stacks of theories about its meaning and assembled viewing tools including <a href="http://vignette1.wikia.nocookie.net/xkcd-time/images/a/a9/Time-animated.gif/revision/latest?cb=20130627164320">animated gif</a>s and GeekWagon’s “<a href="http://geekwagon.net/projects/xkcd1190/index.html?frame=2&amp;framediff=1">Time — at your own pace</a>” viewer, which allows stepping by frame, autoplay with intelligent pausing on important frames (such as ones containing dialogue), and even an option to render the differences between sequential frames.</p><p>“Time” is a stunning example of using the internet itself as an artistic medium. While XKCD at large has its own devoted following, I have quickly come to regard #1190 as a masterpiece of a new kind of art form; it’s vast, yet simple; beautiful in its devotion; and both universal and specific in its interpretation. Like all the best art, at its core, “Time” is about love — and life — and time.</p><p>Whenever you have some time to yourself, I highly recommend spending it on absorbing #1190. Afterwards read <a href="http://blog.xkcd.com/2013/07/29/1190-time/">Randall’s blog post</a> about it, and look into some of the <a href="http://xkcd-time.wikia.com/wiki/XKCD_Time_Wiki?file=Time153.png">fan theories</a> about its possible meanings.</p><ul><li><a href="http://geekwagon.net/projects/xkcd1190/">xkcd Time - at your own pace</a></li><li><a href="http://xkcd-time.wikia.com/wiki/XKCD_Time_Wiki">xkcd Time Wiki</a></li><li><a href="http://blog.xkcd.com/2013/07/29/1190-time/">1190: Time</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=70270becaa2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Binary Operations AND/XOR You.]]></title>
            <link>https://medium.com/@MarkEdwardMurray/binary-operations-and-xor-you-9417a5dc275d?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/9417a5dc275d</guid>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Mon, 09 Mar 2015 12:07:40 GMT</pubDate>
            <atom:updated>2015-03-09T20:24:06.339Z</atom:updated>
            <content:encoded><![CDATA[<p>My curiosity regarding binary operations was piqued last week during an aside our instructor Joe Burgess made during a lecture. In explaining the bitwise operators “&amp;” and “|”, he noted that an early computing trick for calculating multiplication and division by factors of two is simply to shift the binary digits one place forward or one place back. This is a nature of the binary system itself, as depicted in the following example:</p><pre>0000 0101 ( 5, or 5 * 2^0)<br>0000 1010 (10, or 5 * 2^1)<br>0001 0100 (20, or 5 * 2^2)<br>0010 1000 (40, or 5 * 2^3)</pre><p>While this is nifty, it doesn’t explain the normal binary operands that computers use to run their processes. The simplest of which are, of course, addition and subtraction. As I began reading about binary operations, I quickly discovered that there really is a certain magic to it. However, the written expressions that I found for how CPUs solve these problem are fundamentally the same as the long-form arithmetic I was taught as a schoolchild. Addition (and subtraction) in the decimal looks like:</p><pre>        128                      170<br>      +  42                    -  42<br>      -----                    -----<br>        170                      128</pre><p>In binary, this would take the form of:</p><pre>  1000 0000 (128)          1010 1010 (170)<br>+ 0010 1010 ( 42)        - 0010 1010 ( 42)<br>-----------              -----------<br>  1010 1010 (170)          1000 0000 (128)</pre><p>Long form is strikingly simpler to operate by hand in binary than in decimal, since no single-integer arithmetic is required—only that each resulting bit be tested for “0&quot;, “1&quot;, and “carry”. Multiplication and division are a little more complex in current operating systems, but taking the long forms which employ addition and subtraction, these look like:</p><pre>  0000 0000  1001 0010        (146)<br>x 0000 0000  0010 1010         (42)<br>----------------------<br>                     0  (0) <br>          1  0010 010-  (1)   (292)<br>                   0--  (0)<br>+       100  1001 0---  (1) (1,168)<br>----------------------<br>        101  1011 0100      (1,460)<br>                0 ----  (0)<br>+    1 0010  010- ----  (1) (4,672)<br>----------------------<br>  0001 0111  1111 0100      (6,132)</pre><p>And division using subtraction:</p><pre>       ---0 1110 % 0000 0110  (14 remainder 6)<br>     _______________________<br>1010 ) 1001 0010  (146 / 10)<br>     - 1010 ----  (10 * 16 = 160, 160 &gt; 146)<br>     -----------                (0 sixteens)<br>       1001 0010  (146)<br>      - 101 0---  (10 * 8 = 80, 80 &lt; 146)<br>      ----------                (1 eight)<br>        100 0010  (146 - 80 = 66 remainder)<br>       - 10 10--  (10 * 4 = 40, 40 &lt; 66)<br>       ---------                (1 four)<br>          1 1010  (66 - 40 = 26 remainder)<br>        - 1 010-  (10 * 2 = 20, 20 &lt; 26)<br>        --------                (1 two)<br>             110  (26 - 20 = 6 remainder)<br>          - 1010  (10 * 1 = 10, 10 &gt; 6)<br>                                (0 ones)</pre><pre>(146 / 10 = 14 remainder 6)</pre><p>Well, that’s all seemingly straightforward. But how do computers do it? The answer is logic gates.</p><p>A logic gate is the concept of taking two (or more) inputs, and, based upon a specified comparison, outputting a conditional result. In electronics, we know logic gates by their physical form—transistors. Each three-pronged semiconductor element in a CPU represents two input channels and an output, or result, channel. The two logic gates required for binary addition are the AND logic gate, and the XOR logic gate.</p><p>The AND logic gate represents the function <em>A * B</em> or <em>A &amp; B</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/100/1*1OnngQ0ZRkIsk144huc_6g.png" /><figcaption>AND Logic Gate symbol</figcaption></figure><pre>Input       Output<br>A   B       A &amp; B<br>0   0         0<br>1   0         0<br>0   1         0<br>1   1         1</pre><p>While the XOR logic gate outputs whether A and B are <em>different</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/100/1*meLe0YqxuoHVKBdB027djQ.png" /><figcaption>XOR Logic Gate symbol</figcaption></figure><pre>Input      Output<br>A   B      A XOR B<br>0   0      0 (alike)<br>1   0      1 (different)<br>0   1      1 (different)<br>1   1      0 (alike)</pre><p>The simplest combination of these creates the Binary Half-Adder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*7keIOZDwIrLZfqi1hri_fg.png" /><figcaption>The Binary Half Adder</figcaption></figure><p>Here, the XOR gate is used to output the <em>Sum</em> of the input, while the AND gate is used to detect a <em>Carry </em>digit.</p><p>By including use of an OR logic gate, we can create the Binary Full-Adder, which can track the status of the carry digit.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/100/1*zXNYQZThvj-_gQF58Md3rg.png" /><figcaption>OR Logic Gate symbol</figcaption></figure><pre>Input      Output<br>A   B      A OR B<br>0   0        0<br>1   0        1<br>0   1        1<br>1   1        1</pre><p>In the Full-Adder, first <em>A &amp; B</em> are compared, then their result is compared to the <em>Carry-in</em> to find the final <em>Sum</em>. At the same time, if<em> A &amp; B</em> equals 1, or if <em>(A &amp; B) &amp; C</em> equals 1, then the <em>Carry-out</em> results as 1.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*lzU1iiE3O09BXlDSSRxFdA.png" /><figcaption>The Binary Full Adder</figcaption></figure><pre>  Input         Output<br>A   B  C-in    Sum C-out<br>0   0   0       0    0<br>1   0   0       1    0<br>0   1   0       1    0<br>1   1   0       0    1</pre><pre>0   0   1       1    0<br>1   0   1       0    1<br>0   1   1       0    1<br>1   1   1       1    1</pre><p>The Full-Adder can be repeated to create multi-bit adders such as the Ripple-Carry-Adder (4-bit) depicted here. More efficient constructions of multi-bit adders are the Carry-Lookahead Adder and the Cascading Adder, which allow for selected skipping of the logic gates in the carry block (more on this <a href="http://en.wikibooks.org/wiki/Microprocessor_Design/Add_and_Subtract_Blocks">here</a>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*VFEP1gt8H6A8EJkKj1ZS7Q.png" /><figcaption>A 4-bit Ripple Carry Adder. Created by <a href="http://By en:User:Cburnett [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], via Wikimedia Commons">User: cburnett</a> , <a href="http://commons.wikimedia.org/wiki/File%3A4-bit_ripple_carry_adder.svg">Wikimedia Commons.</a></figcaption></figure><p>But wait, now we’re using a third input which transistors don’t have…</p><p>Well, this is a logic diagram which can built using transistors arranged in a specific way. In considering this, let me point out that there’s something special about another kind of logic gate called the NAND gate. The NAND gate, and its sibling the NOR gate, are referred to as the <em>universal logic gates</em> because either one can be combined with itself in ways to replicated the function of any of the other logic gates.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/100/1*JJZQoa0bMjhIPBMHEDT_Jg.png" /><figcaption>NAND Logic Gate symbol</figcaption></figure><pre>Input      Output<br>A   B      A NAND B<br>0   0         1<br>1   0         1<br>0   1         1<br>1   1         0</pre><p>So, if we string together a set of NAND gates, we can create any kind of gate we want in order to perform any function we want.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*Yw3I8GkQNCTbOafRueYgaQ.png" /><figcaption>XOR Logic Gate using NAND construction</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/220/1*rXZu9H-YilofWnDH44A-rw.jpeg" /><figcaption>The 7400 chip, containing four NANDs.</figcaption></figure><p>Can you see the implementation of these logical principals in the TTL 7400 chipset?</p><p>Exactly how CPUs structure their transistors to perform the desired arithmetic is beyond both the scope of my current understanding and the scope of this blog post.</p><p>However, knowing now what we’ve learning about logic gates, let’s take another look at that binary addition.</p><pre>  1000 0000 (128)          1010 1010 (170)<br>+ 0010 1010 ( 42)        - 0010 1010 ( 42)<br>-----------              -----------<br>          0   0 XOR 0              0    0 XOR 0<br>         1    0 XOR 1             0     0 XOR 0<br>        0     0 XOR 0            0      0 XOR 0<br>       1      0 XOR 1           0       1 XOR 1<br>     0        0 XOR 0         0         0 XOR 0<br>    1         0 XOR 1        0          1 XOR 1<br>   0          0 XOR 0       0           0 XOR 0<br>  1           1 XOR 0      1            1 XOR 0<br>-----------              -----------<br>  1010 1010 (170)          1000 0000 (128)</pre><p>Neither of these examples illustrate use of the carry function, which in subtraction is reversed by using the NOT logic gate. However, I hope that I’ve successfully illustrated the connection between binary math and the use of logic gates in computer architecture. For further reading on these concepts, please follow my reference links.</p><p><em>Unless otherwise noted, all diagrams are sourced from Wikipedia and republished here under open content licenses.</em></p><ul><li><a href="http://en.wikipedia.org/wiki/Logic_gate">Logic gate</a></li><li><a href="http://www.electronics-tutorials.ws/combination/comb_7.html">Binary Adder and Binary Addition using Ex-OR Gates</a></li><li><a href="http://en.wikibooks.org/wiki/Microprocessor_Design/Add_and_Subtract_Blocks">Microprocessor Design/Add and Subtract Blocks</a></li><li><a href="http://en.wikipedia.org/wiki/NAND_gate">NAND gate</a></li><li><a href="http://en.wikipedia.org/wiki/Adder_(electronics)">Adder (electronics)</a></li><li><a href="http://en.wikipedia.org/wiki/Binary_number#Binary_arithmetic">Binary number</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9417a5dc275d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Objective: C (change the world)]]></title>
            <link>https://medium.com/@MarkEdwardMurray/objective-c-change-the-world-7803f55ef70e?source=rss-1b005c721fda------2</link>
            <guid isPermaLink="false">https://medium.com/p/7803f55ef70e</guid>
            <dc:creator><![CDATA[Mark Edward Murray]]></dc:creator>
            <pubDate>Wed, 18 Feb 2015 13:38:03 GMT</pubDate>
            <atom:updated>2015-02-18T13:38:03.545Z</atom:updated>
            <content:encoded><![CDATA[<p>Bill Nye is one of my heroes. Like many young professionals in my age group, I grew up watching his science education show. I didn’t realize it as a child, but between the explanations, demonstrations, and cheesy pop-covers which tagged the end of every episode, what I was really learning is that learning should be fun. And it is. In fact, it should be.</p><p>I was fortunate enough to grow up with two parents who fostered creative learning. I realize not every child has this. I didn’t know at the time, but all of the science kits, Lego sets, field trips, computer games, and patient explanations helped me develop what is perhaps my most cherished quality: a curious mind.</p><p>You see, I was allowed to wonder. And in that wonder, to discover. I can recall the satisfaction I felt upon assembling a crystal AM radio kit I had bought (or, rather, my parents had bought) at a Boy Scout camp: a spool of copper wire wrapped around a cardboard tube and frailly connected to a 9V battery, a tiny piece of quartz, and an ear piece which quickly became a science experiment of another kind.</p><p>It was rudimentary, and honestly it didn’t work very well, but I remember the first time I heard the ocean of white static crack and pop into the faint glimmer of a tune. Here was something I had made with my own hands—from raw, unconnected sinews—that connected invisibly to the outside world, to the infrastructure crafted by adults.</p><p>No longer just an ordinary child, I was now a participant. Not just an idle, unknowing consumer of the magic once called “radio,” I knew how it worked. And I knew why it worked. I held its secret in my mind. If the radio could be called magic, then I had become its sorcerer.</p><p>There was a time when I believed that to be somebody—to really be someone and to accomplish things and be worthwhile, I had to become famous. It wasn’t really the fame itself I craved (I’m ironically quite shy in public), but rather, I sought the affirmation I expected would come with it.</p><p>Perhaps it was all the movies that my family watched growing up that led me to look for a different reality inside the screen. I don’t fully know. But as a teenager I gave up my path of scientific sorcery to study acting and the humanities. I chose a Theatre major and practiced Stanislavski, argued over Strindberg, watched Becket and O’Neill, read Chaucer and Donne, Frost and Dickinson, performed Moliére and produced Brecht, all while immersing myself in the vast, rich depths of the English Bard.</p><p>From all of this, I learned a funny thing about stories: they’re very mathematical. The scansions of iambic pentameter betray the author’s intent. Beowulf’s 101 stanzas are perfectly mirrored rising and falling action. Every story, every act, every scene, every moment contains a beginning, a middle, and an end: perpetual, nested trinities to the Nth degree.</p><p>The very fabric of language is algorithm.</p><p>Messages or verbs, receivers or direct objects; past, present, future, pluperfect; predicates, descriptors, syntax, operands, and punctuation—a programming language is just that: a language. On a fundamental level, learning to write computer code is not so different from second-language acquisition: it is just another way of communicating thought.</p><p>Back in December I had the pleasure of seeing Mr. Nye speak in the Upper East Side right here in Manhattan. It was slightly unreal to me—here was this formative figure from my childhood, of whom I’d only ever seen flat images on a screen, now lumbering awkwardly onto stage in the form of a lanky yet delightfully bow-tied hominid.</p><p>He was there to discuss his recent book “Undeniable” which was passionately inspired by the importance to him of science education and fostering independent thought in schools. It was this same passion which had inspired his TV show two decades ago—the desire to make learning fun, to bring science and math out of the rigidity of a textbook and allowing it to intertwine with the whole complexity of the human experience.</p><p>Here was this man with a household name, simultaneously aware of and innocent from his fame, running off on tangents about how amazing dirt can be—about how everything we’ve ever seen or known or touched was at one time held in the crucible of a star—about how incredibly vast the earth’s biodiversity is—about how “we are, you and I, at least one of the ways that the universe knows itself.”</p><p>Those words were him paraphrasing his own mentor Carl Sagan, and they’ve echoed in my mind over the last year. Twenty years ago Bill settled upon his idea to change the world in a way that only he could.</p><p>Now, it’s my turn to settle upon my own.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7803f55ef70e" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>