NetNewsWire/Frameworks/Account/AccountTests/Feedly/feedly-2-changestatuses/starred.json

1184 lines
284 KiB
JSON

{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"items": [
{
"originId": "tag:blogger.com,1999:blog-8954608646904080796.post-8772112161384908748",
"fingerprint": "80075e70",
"thumbnail": [
{
"url": "https://1.bp.blogspot.com/-pMon836tLyo/XatsZ0Tv9MI/AAAAAAAADTs/90dBB5-ohkk2yOW4--zh7M0p_JicFuC4QCLcBGAsYHQ/s72-c/017.jpg",
"width": 72,
"height": 72
}
],
"id": "v0v+7Ya8tssIZvd3/pcnFRr3HwvY/5YK3FGc2t65c0Y=_16de5ccc6bb:ffbc:d4506071",
"updated": 1571515782582,
"author": "Edward Feser",
"alternate": [
{
"href": "http://edwardfeser.blogspot.com/2019/10/masculinity-and-marvel-movies.html",
"type": "text/html"
}
],
"crawled": 1571518465723,
"title": "Masculinity and the Marvel movies",
"published": 1571515740000,
"origin": {
"streamId": "feed/http://edwardfeser.blogspot.com/feeds/posts/default",
"htmlUrl": "http://edwardfeser.blogspot.com/",
"title": "Edward Feser"
},
"content": {
"direction": "ltr",
"content": "<br><div><a href=\"https://1.bp.blogspot.com/-pMon836tLyo/XatsZ0Tv9MI/AAAAAAAADTs/90dBB5-ohkk2yOW4--zh7M0p_JicFuC4QCLcBGAsYHQ/s1600/017.jpg\"><img border=\"0\" src=\"https://1.bp.blogspot.com/-pMon836tLyo/XatsZ0Tv9MI/AAAAAAAADTs/90dBB5-ohkk2yOW4--zh7M0p_JicFuC4QCLcBGAsYHQ/s200/017.jpg\" data-original-width=\"255\" width=\"200\" data-original-height=\"163\" height=\"127\"></a></div><div><span>Some time back, John Haldane gave a Thomistic Institute talk here in Los Angeles on the theme of <span><a href=\"https://soundcloud.com/thomisticinstitute/john-haldane-darkness-in-the-city-of-angels-evil-as-a-theme-and-vice-as-a-fact?utm_source=soundcloud&utm_campaign=share&utm_medium=facebook\">evil in the movies and in the movie industry</a></span>.\u00a0 During the Q and A (at about the 40 minute mark, and again after the 1:16 mark) the subject of superhero movies came up, and Haldane was critical of their current prevalence.\u00a0 In developing this criticism, he draws a useful distinction between <i>fantasy</i>and <i>imagination</i>.</span></div><a name=\"more\"></a> \u00a0<br> <div><span>Imagination, as Haldane uses the term, is a way of exploring aspects of reality and possibilities that are grounded in reality, even though it makes use of scenarios that are fictional or even impossible.<span>\u00a0 </span>Imagination is healthy and can increase our understanding of the moral and social worlds.<span>\u00a0 </span>Fantasy, by contrast, is unanchored in reality, and indeed it reflects a flight from reality and the discipline it imposes and responsibility that it entails.<span>\u00a0 </span>Haldane gives as an example the movie <i>Pretty Woman</i>, an absurdly unrealistic portrayal of prostitution and human relationships.</span></div><div><span><br></span></div><div><span>Fantasy can be harmless in small doses, Haldane allows, but when a culture becomes dominated by it, that is a sign that it has become decadent and unwilling to face reality.<span>\u00a0 </span>And the prevalence of superhero movies, Haldane says, is an indication that American society is increasingly retreating into fantasy and away from reality.<span>\u00a0 </span>He rejects the suggestion that such movies can be compared to the myths of the gods in ancient cultures.<span>\u00a0 </span>Such myths, he says, are essentially exercises in imagination, whereas superhero movies are sheer fantasy.</span></div><div><span><br></span></div><div><span>I think there is some truth to this analysis, but only some.<span>\u00a0 </span>Some superhero movies are indeed exercises in fantasy, but some are, in my view, clearly exercises in imagination.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Not long after hearing Haldane\u2019s talk, I happened to come across <span><a href=\"https://www.youtube.com/watch?v=T6wp0Qbf4Cw\">a 1978 television interview</a></span> with the late Harlan Ellison during which (beginning just before the 5 minute mark) Ellison criticizes the movies <i>Close Encounters of the Third Kind</i> and <i>Star Wars,</i> and modern American society in general, on exactly the same grounds raised by Haldane.<span>\u00a0 </span>He doesn\u2019t use Haldane\u2019s terminology, and in fact partially inverts it.<span>\u00a0 </span>Ellison uses \u201cfantasy\u201d to mean what Haldane means by \u201cimagination,\u201d and he uses the expression \u201cspace opera\u201d to refer to one type of what Haldane calls \u201cfantasy.\u201d<span>\u00a0 </span>But in substance, the distinction and the sort of points Haldane and Ellison are making are identical.</span></div><div><span><br></span></div><div><span>(Side note: Remember when you could find extended intelligent discussion like this on television?<span>\u00a0 </span>Remember when you could casually smoke on television, as Ellison does during the interview?<span>\u00a0 </span>Remember Laraine Newman, another guest on the show who also contributes to the discussion?)</span></div><div><span><br></span></div><div><span>Interestingly, though, Ellison was also well-known to be an enthusiast for comics, including superhero comics, and even wrote them from time to time (though this doesn\u2019t come up in the interview).<span>\u00a0 </span>I don\u2019t think there is any inconsistency there.</span></div><div><span><br></span></div><div><span>Suppose that, like me and like Haldane (though unlike Ellison) you are a conservative Catholic.<span>\u00a0 </span>Then, I would suggest, it is easy to see that there are themes in many superhero movies, and especially in the <i>Avengers</i> series that is currently the most popular of all, that are clearly reflections of imagination rather than fantasy.</span></div><div><span><br></span></div><div><span>Take the characters who, in the <i>Avengers</i> movies as in the comics, have been regarded as \u201cthe Big Three\u201d: Captain America, Thor, and Iron Man.<span>\u00a0 </span>Captain America represents patriotism, the military virtues, the earnest decency of the common man, and in general a Norman Rockwell style nostalgia for a simpler time.<span>\u00a0 </span>Thor \u2013 as part of the Asgardian pantheon ruled by stern Odin, to whom he must prove his worthiness \u2013 represents the higher realm spoken of by religion, and our obligations to the divine patriarchal authority who governs it.<span>\u00a0 </span>Iron Man is a business magnate who represents confident masculinity, superior ability and great wealth, and the <i>noblesse oblige</i>and rebuke to egalitarianism implied by them.<span>\u00a0 </span>These are deeply conservative themes, and it is astounding that these characters are as popular as they are in a society increasingly suffocated by political correctness. </span></div><div><span><br></span></div><div><span>Or maybe not.<span>\u00a0 </span>For such themes have appeal because they reflect human nature, and human nature does not change however much we try to paper over it with ideology and propaganda, and however corrupt human behavior and human societies become as a result.<span>\u00a0 </span>People will yearn in at least an inchoate way for the traditional institutions and ideals without which they cannot fulfill their nature, even when they are told they ought not to and have halfway convinced themselves that they ought not to.</span></div><div><span><br></span></div><div><span>I would suggest that the Marvel movies have the appeal they do at least in part precisely because they both convey these traditional ideals, but do so in a way that is fantastic enough that the offense to political correctness is not blatant.<span>\u00a0 </span>A film series whose heroes are a square patriotic soldier, the son of a heavenly Father come to earth, and a strutting capitalist alpha male sounds like something tailor-made for a Red State audience, and the last thing that would attract A-list actors and billions in investment from a major studio.<span>\u00a0 </span>Put these characters in colorful costumes, scenarios drawn from science fiction, and a little PC window dressing (such as portraying their girlfriends as a soldier, a scientist, and a businesswoman, respectively), and suddenly even a Blue State crowd can get on board.</span></div><div><span><br></span></div><div><span>Now, there are no traditional ideals more battered in contemporary Western society than masculinity, and the paternal role that is the fulfilment of masculinity.<span>\u00a0 </span>But these are precisely the key themes of many of the Marvel movies.<span>\u00a0 </span>The longing for a lost father or father figure is the core of all of the Spider-Man movies, as I noted in <span><a href=\"https://edwardfeser.blogspot.com/2014/07/marvel-team-up-spider-man-and-patriarchy.html\">a post from a few years back</a></span>.<span>\u00a0 </span>(In the Spider-Man movies that have appeared since that post was written, Tony Stark has become the father figure whose instruction and example Peter Parker strives to live up to.)<span>\u00a0 </span>The theme is also central to the <i>Guardians of the Galaxy</i> series, to <i>Black Panther</i>, to the <i>Daredevil</i> movie and Netflix series, and to the <i>Luke Cage</i> and <i>Iron Fist</i> Netflix series.<span>\u00a0 </span>The <i>Thor</i>movies are largely about the conflicted relationships Thor and Loki have with their father Odin, whose approval each of them nevertheless seeks.<span>\u00a0 </span>The bad consequences of <i>rebellion</i> against a father or father figure is the theme of the original Spider-Man series (wherein Peter initially refuses to heed his Uncle Ben\u2019s admonitions), of the first <i>Thor</i>movie, and of <i>Avengers: Age of Ultron</i>(whose wayward son is the robotic Ultron, at odds with his \u201cfather\u201d Stark). </span></div><div><span><br></span></div><div><span>The <i>Hulk</i> movies are largely about the consequences of failure as a father (whether Bruce Banner\u2019s father in the original <i>Hulk</i> movie, or Betty Ross\u2019s father in <i>The Incredible Hulk</i>).<span>\u00a0 </span><i>Ant-Man</i>is essentially about two men (Scott Lang and Hank Pym) who have partially failed as fathers and are trying to make up for it.<span>\u00a0 </span>The <i>Punisher </i>Netflix series is essentially about a husband and father seeking vengeance for the family that was taken from him.</span></div><div><span><br></span></div><div><span>But it is the two stars of the Marvel movies \u2013 Tony Stark/Iron Man and Steve Rogers/Captain America \u2013 who are the most obvious examples of idealized masculinity.<span>\u00a0 </span>And their character arcs through the series are about realizing that ideal.<span>\u00a0 </span>Each of them starts out as an imperfect specimen of the masculine ideal, albeit in very different ways.<span>\u00a0 </span>With Stark it is a vice of deficiency and with Rogers it is a vice of excess.<span>\u00a0 </span>But by the end of their arcs, in <i>Avengers: Endgame</i>, each achieves the right balance.<span>\u00a0 </span>(It might seem odd to think of <i>Rogers</i>rather than Stark as the one prone to a kind of excess.<span>\u00a0 </span>Bear with me and you\u2019ll see what I mean.)</span></div><div><span><br></span></div><div><span>On the traditional understanding of masculinity, a man\u2019s life\u2019s work has a twofold purpose.<span>\u00a0 </span>First, it is ordered toward providing for his wife and children.<span>\u00a0 </span>Second, it contributes something distinctive and necessary to the larger social order of which he and his family are parts.<span>\u00a0 </span>Society needs farmers, butchers, tailors, manual laborers, soldiers, scholars, doctors, lawyers, etc. and a man finds purpose both by being a husband and father and by filling one of these social roles.<span>\u00a0 </span>Though the traditional view regards women as \u201cthe weaker sex\u201d and as less assertive than men, it understands a man\u2019s worth and nobility in terms of the extent to which his strength and assertiveness are directed toward the <i>service of others</i>.</span></div><div><span><br></span></div><div><span>Liberal individualism, both in its libertarian form and its egalitarian form, replaced this social and other-directed model of a man\u2019s life\u2019s work with an individualist and careerist model, on which work is essentially about self-expression and self-fulfillment \u2013 making one\u2019s mark in the world, gaining its attention and adulation, attaining fame, power and influence, and so forth. <span>\u00a0</span>Nor is it even about providing for wife and children, since sex and romance too came to be regarded as a means of self-fulfillment rather than the creation of the fundamental social unit, the family.<span>\u00a0 </span>(Feminism took this corrupted individualist understanding of the meaning of a man\u2019s work and relationships and, rather than critiquing it, urged women to ape it as well.)</span></div><div><span><br></span></div><div><span>In the first two <i>Iron Man</i> movies, Stark is initially a specimen of this individualist mentality.<span>\u00a0 </span>His work is oriented toward attaining wealth, fame, and power.<span>\u00a0 </span>He uses women as playthings.<span>\u00a0 </span>He has a conflicted relationship with his late father, and is contemptuous of authority in general.<span>\u00a0 </span>He is judged by SHIELD to be \u201cvolatile, self-obsessed, and [unable to] play well with others.\u201d<span>\u00a0 </span>But he is gradually chastened by the consequences of his hubris \u2013 by being captured and injured in the first <i>Iron Man</i> movie; by being forced to face up to the limitations on his power to stop an alien invasion like the one that occurred in <i>Avengers</i>; and by the miscalculation that led to Ultron\u2019s rebellion and the many deaths it caused.<span>\u00a0 </span>By <i>Captain America: Civil War</i>, Stark is humbled enough to accept government oversight, and being left defeated and near-dead by Thanos in <i>Avengers: Infinity War</i> completes his chastening.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>By <i>Avengers: Endgame</i> Stark has become a family man.<span>\u00a0 </span><span>\u00a0</span>By way of time travel, he makes peace of a sort with his father.<span>\u00a0 </span>In the first <i>Avengers</i> movie, he had casually dismissed Rogers\u2019 talk of the need for self-sacrifice with the confidence that an alternative solution would always be possible for a clever person like himself.<span>\u00a0 </span>By contrast, in <i>Endgame</i>, he sees that he needs to lay down his life in order to save his wife and daughter and the world in general, and he willingly does it.<span>\u00a0 </span>To be sure, he is in no way neutered. <span>\u00a0</span>He retains his masculine assertiveness, strength, and self-confidence.<span>\u00a0 </span>But they are now directed toward the service of something larger than himself.</span></div><div><span><br></span></div><div><span>Rogers, by contrast, is from the first <i>Captain America</i> movie onward driven by a sense of duty to his country and to the social order more generally, and is willing to sacrifice everything for it, including even his own happiness and indeed his own life.<span>\u00a0 </span>He is also a perfect gentleman, and his only interest where women are concerned is with the one he would like to marry and settle down with if only he had the chance.<span>\u00a0 </span>Like Stark, he is relentlessly assertive, confident, and competent, but unlike Stark these traits are from the start directed toward the service of a larger good.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Rogers\u2019 flaw is that he is if anything a bit <i>too</i>absorbed in this larger good.<span>\u00a0 </span>At least initially, he is too much the man of action and the good soldier, with all the virtues but also with the flaws that that entails.<span>\u00a0 </span>He is a little too deferential to authority.<span>\u00a0 </span>In the first <i>Avengers</i> movie he glibly asserts: \u201cWe have orders.<span>\u00a0 </span>We should follow them\u201d \u2013 only to find out that perhaps he should have questioned them.<span>\u00a0 </span>The way institutions and authorities can become corrupted is impressed upon him far more dramatically in <i>Captain America: Winder Soldier</i>, to the point that in <i>Civil War</i> it is <i>Rogers</i> who is urging <i>Stark</i> to be more skeptical of authority.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>In general, Rogers\u2019 optimistic \u201ccan do\u201d spirit sometimes borders on na\u00efvet\u00e9, and it takes the catastrophe of <i>Infinity War</i> to teach him that the good guys don\u2019t always win and that some problems can only be managed or mitigated rather than solved.<span>\u00a0 </span>For much of the series, Rogers also has little life outside some military or quasi-military organization \u2013 the army, SHIELD, the Avengers.<span>\u00a0 </span>Without a war to fight, he doesn\u2019t know what to do with himself.<span>\u00a0 </span>He is square, prone to speechifying, and awkward with women \u2013 in <i>Winter Soldier</i> proclaiming himself \u201ctoo busy\u201d for romance, preferring to lose himself in one mission after the other.<span>\u00a0 </span>Only after near-death and victory in a \u201cmother of all battles\u201d in <i>Endgame</i>does he become convinced that he has the right to retire and \u201ctry some of that life Tony was telling me to get\u201d \u2013 traveling back in time to marry the woman he thought he\u2019d lost forever.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>The theme of the parallels and differences between the two characters provides a backbone to the Marvel movies.<span>\u00a0 </span>Both Stark and Rogers are supremely confident and competent.<span>\u00a0 </span>They are both natural leaders.<span>\u00a0 </span>Each stubbornly insists on pursuing the course he is convinced is the correct one.<span>\u00a0 </span>They are too similar in these respects \u2013 though also too different in the other ways just described \u2013 to like each other much at first.<span>\u00a0 </span>The world is not big enough for both egos.<span>\u00a0 </span>They learn to like and respect each other only gradually, through many ups and downs.</span></div><div><span><br></span></div><div><span>Hence, in the first <i>Avengers </i>movie, Stark is jealous of the admiration that his father had had for Rogers, and Rogers is amazed that Howard Stark could have had a son as frivolous and unworthy as Tony.<span>\u00a0 </span>By <i>Civil War</i>, Rogers ends up having to defend the man who had (under mind control) murdered Howard \u2013 defending him from Tony, who seeks to avenge his father and now (temporarily) judges <i>Rogers</i> unworthy of his father\u2019s admiration.<span>\u00a0 </span>Stark starts out arrogantly rejecting any government control over his activity as Iron Man, only to insist on government control in <i>Civil War</i>.<span>\u00a0 </span>Rogers starts out dutifully following orders in the first <i>Captain America</i> and <i>Avengers</i> movies, only stubbornly to reject government control over the team in <i>Civil War</i>.<span>\u00a0 </span>In <i>Age of Ultron</i>, Rogers criticizes Stark for acting independently of the team, and in <i>Civil War</i>, Stark criticizes Rogers for acting independently of the team.<span>\u00a0 </span>Rogers feels guilt for failing to prevent the death of Bucky, his comrade-in-arms.<span>\u00a0 </span>Stark feels guilt for failing to prevent the death of Peter Parker, to whom he has become a father figure.<span>\u00a0 </span>Rogers lays down his life in the first <i>Captain America</i> movie, only to get it back.<span>\u00a0 </span>Stark preserves his life against all odds throughout the whole series, only to lay it down in the last <i>Avengers</i> movie.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>I submit that its complex portrayal of these competing models of masculinity is part of what makes the Marvel series of movies a genuine exercise in <i>imagination</i> rather than fantasy, in Haldane\u2019s sense of the terms.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>One wonders, however, whether this will last.<span>\u00a0 </span>A few years ago, Marvel\u2019s comics division notoriously reoriented their titles to reflect greater \u201cdiversity\u201d and political correctness \u2013 an experiment that critics labeled \u201cSJW Marvel\u201d and that <span><a href=\"https://thefederalist.com/2017/04/12/forcing-political-correctness-employees-characters-killing-marvel-comics/\">resulted in a dramatic decline in sales</a></span>.<span>\u00a0 </span>The trend has been partially reversed and did not at the time affect the movies, where much more money is at stake.<span>\u00a0 </span>But <a href=\"https://thefederalist.com/2019/09/05/next-slate-marvel-movies-feature-characters-dulled-identity-politics/\">there are signs</a> that a milder form of the \u201cSJW Marvel\u201d approach will make its way into the Marvel Cinematic Universe in the next phase of movies.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>For example, the title character of <i>Captain Marvel</i>is portrayed with little emotion, no love interest, and lacking any of the femininity, vulnerability, and complexity of characters like Scarlett Johannsen\u2019s Black Widow or Elizabeth Olsen\u2019s Scarlet Witch.<span>\u00a0 </span>As Kyle Smith <a href=\"https://www.nationalreview.com/2019/03/captain-marvel-movie-review-dull-directionless/\">noted in <i>National Review</i></a>, Brie Larson portrays her instead as \u201cfiercer than fierce, braver than brave\u2026 insouciant, kicking butt, delivering her lines in an I-got-this monotone\u2026 amazingly strong and resilient at the beginning, middle, and end. <span>\u00a0</span>This isn\u2019t an arc, it\u2019s a straight line.\u201d<span>\u00a0 </span>Into the bargain, this C-list character, dropped into the Marvel Cinematic Universe out of nowhere, is <a href=\"https://www.looper.com/147990/the-mcu-most-powerful-character-is-confirmed-captain-marvel/\">suddenly proclaimed</a> \u201cthe most powerful character\u201d in that universe.</span></div><div><span><br></span></div><div><span>In short, <i>Captain Marvel</i> is transparently an exercise in feminist wish fulfilment.<span>\u00a0 </span>More to the present point, it is sheer <i>fantasy</i> in Haldane\u2019s sense, rather than imagination \u2013 a portrayal of the way a certain mindset <i>wishes</i> the world to be, rather than a fanciful representation of the way it really is.<span>\u00a0 </span>And, as Smith points out, its title character is for that reason completely boring.<span>\u00a0 </span>(Contrast this with Marvel\u2019s Netflix series <i>Jessica Jones</i>, which \u2013 despite its own feminist undercurrents \u2013 is <i>not</i> boring, and whose female characters are well-rounded and interesting.)<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>If future Marvel movies follow in this identity politics oriented direction, they will in fact become what Haldane (in my view mistakenly) thinks they already are.</span></div><div><span><br></span></div><div><span>Further reading:</span></div><div><span><br></span></div><div><span><a href=\"https://edwardfeser.blogspot.com/2011/09/pop-culture-roundup.html\">Pop culture roundup</a></span></div>"
},
"unread": true,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/fbdcd69b-7e27-4b6a-bfed-6584b944155d",
"label": "\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572578985412
},
{
"originId": "tag:blogger.com,1999:blog-8954608646904080796.post-8613145924738251081",
"fingerprint": "473d1ae4",
"thumbnail": [
{
"url": "https://1.bp.blogspot.com/-P_7aCRZVVKo/XbollnDDTuI/AAAAAAAADUU/y6TgUEudc0EU89QnGt1IxZ1Frdro3GwVQCLcBGAsYHQ/s72-c/047.jpg",
"width": 72,
"height": 72
}
],
"id": "v0v+7Ya8tssIZvd3/pcnFRr3HwvY/5YK3FGc2t65c0Y=_16e1f35113f:1cfaa:d4506071",
"updated": 1572480433981,
"author": "Edward Feser",
"alternate": [
{
"href": "http://edwardfeser.blogspot.com/2019/10/new-from-editiones-scholasticae.html",
"type": "text/html"
}
],
"crawled": 1572481601855,
"title": "New from Editiones Scholasticae",
"published": 1572480420000,
"origin": {
"streamId": "feed/http://edwardfeser.blogspot.com/feeds/posts/default",
"htmlUrl": "http://edwardfeser.blogspot.com/",
"title": "Edward Feser"
},
"content": {
"direction": "ltr",
"content": "<br><div><a href=\"https://1.bp.blogspot.com/-P_7aCRZVVKo/XbollnDDTuI/AAAAAAAADUU/y6TgUEudc0EU89QnGt1IxZ1Frdro3GwVQCLcBGAsYHQ/s1600/047.jpg\"><img border=\"0\" src=\"https://1.bp.blogspot.com/-P_7aCRZVVKo/XbollnDDTuI/AAAAAAAADUU/y6TgUEudc0EU89QnGt1IxZ1Frdro3GwVQCLcBGAsYHQ/s200/047.jpg\" data-original-width=\"251\" width=\"200\" data-original-height=\"180\" height=\"143\"></a></div><div><span>Editiones Scholasticae, the publisher of my books <span><i><a href=\"https://www.amazon.com/Scholastic-Metaphysics-A-Contemporary-Introduction/dp/3868385444/ref=sr_1_1?ie=UTF8&qid=1391482601&sr=8-1&keywords=scholastic+metaphysics+a+contemporary+introduction\">Scholastic Metaphysics</a></i></span> and <span><i><a href=\"https://www.amazon.com/Aristotles-Revenge-Metaphysical-Foundations-Biological/dp/3868382003/\">Aristotle\u2019s Revenge</a></i></span>, informs me that both of them will within a few days be available in eBook versions.\u00a0 Also new from the publisher is a German translation of my book <span><i><a href=\"https://www.editiones-scholasticae.de/index.php?page=shop.product_details&flypage=flypage.tpl&product_id=452&category_id=31&keyword=feser&option=com_virtuemart&Itemid=19\">Philosophy of Mind</a></i></span>. \u00a0(Previously they had published German translations of <span><i><a href=\"https://www.editiones-scholasticae.de/index.php?page=shop.product_details&flypage=flypage.tpl&product_id=371&category_id=31&keyword=feser&option=com_virtuemart&Itemid=19\">The Last Superstition</a></i></span> and <span><i><a href=\"https://www.editiones-scholasticae.de/index.php?page=shop.product_details&flypage=flypage.tpl&product_id=440&category_id=31&keyword=feser&option=com_virtuemart&Itemid=19\">Five Proofs of the Existence of God</a></i></span>.) \u00a0Take a look at <span><a href=\"https://www.editiones-scholasticae.com/\">Editiones Scholasticae\u2019s new webpage</a></span> for further information, as well as for information about other new releases from the publisher.\u00a0 You will find both new works by contemporary writers in the Scholastic tradition, and reprints of older and long out of print works in that tradition.\u00a0 (The original webpage <span><a href=\"https://www.editiones-scholasticae.de/\">is still online as well</a></span>.)</span></div>"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/qEtBNTmNKQvUro7McqHPXL-2OOM=/0x0:6720x4480/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59664621/marshall_majorIII_aniconinthemaking_product_rgb_highres_2.0.jpg",
"width": 1310,
"height": 878,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 2725,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/fbdcd69b-7e27-4b6a-bfed-6584b944155d",
"label": "\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.unsaved",
"label": "Unsaved"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572578978618
},
{
"keywords": [
"Apple",
"Business"
],
"originId": "http://www.loopinsight.com/?p=100501",
"fingerprint": "5554fc75",
"id": "A4VVV8iwf2HD21qpAa7ABDI8MY8lm8dIbsVbE6sVKNA=_16e226a819a:1e01f:d4506071",
"author": "Dave Mark",
"summary": {
"direction": "ltr",
"content": "Kif Leswing, CNBC:\n<blockquote>\nUnder the argument for an iPhone subscription, which some people call Apple Prime after the Amazon program of the same name, Apple would bundle hardware upgrades with services like iCloud storage or Apple TV+ content and hardware for a single monthly fee. This would let it switch iPhone sales from a transactional model to a subscription model, potentially driving the stock price up without having to increase product sales or prices dramatically.\n</blockquote>\nAnd:\n<blockquote>\n\u2033In terms of hardware as a service or as a bundle, if you will, there are customers today that essentially view the hardware like that because they\u2019re on upgrade plans and so forth,\u201d Cook said during an earnings call. \u201cSo to some degree that exists today.\u201d\n</blockquote>\nAnd, most importantly:\n<blockquote>\n\u201cMy perspective is that will grow in the future to larger numbers. It will grow disproportionately\u201d\n</blockquote>\nI had the chance to be on John Gruber's show (recorded yesterday, guessing it'll drop today or tomorrow, assuming John is not too horrified with the results), and we were talking about this, peripherally. John mentioned the future possibility of Apple Prime, a concept similar to Amazon Prime. From the article:\n<blockquote>\nUnder the argument for an iPhone subscription, which some people call Apple Prime after the Amazon program of the same name, Apple would bundle hardware upgrades with services like iCloud storage or Apple TV+ content and hardware for a single monthly fee.\n</blockquote>\nI suspect we'll all eventually be subscribing from a menu of services, including column A, software, column B, traditional services, and column C, hardware. Intriguing.<a rel=\"bookmark\" href=\"https://www.loopinsight.com/2019/10/31/apple-is-laying-the-groundwork-for-an-iphone-subscription/\" title=\"Permanent link to 'Apple is laying the groundwork for an iPhone subscription'\">\u221e Read this on The Loop</a>"
},
"alternate": [
{
"href": "https://www.cnbc.com/2019/10/30/apple-lays-groundwork-for-iphone-or-apple-prime-subscription.html",
"type": "text/html"
}
],
"crawled": 1572535435674,
"title": "Apple is laying the groundwork for an iPhone subscription",
"published": 1572532001000,
"origin": {
"streamId": "feed/http://www.loopinsight.com/feed/",
"htmlUrl": "https://www.loopinsight.com",
"title": "The Loop"
},
"content": {
"direction": "ltr",
"content": "<p>Kif Leswing, CNBC:</p>\n<blockquote>\n<p>Under the argument for an iPhone subscription, which some people call Apple Prime after the Amazon program of the same name, Apple would bundle hardware upgrades with services like iCloud storage or Apple TV+ content and hardware for a single monthly fee. This would let it switch iPhone sales from a transactional model to a subscription model, potentially driving the stock price up without having to increase product sales or prices dramatically.</p>\n</blockquote>\n<p>And:</p>\n<blockquote>\n<p>\u2033In terms of hardware as a service or as a bundle, if you will, there are customers today that essentially view the hardware like that because they\u2019re on upgrade plans and so forth,\u201d Cook said during an earnings call. \u201cSo to some degree that exists today.\u201d</p>\n</blockquote>\n<p>And, most importantly:</p>\n<blockquote>\n<p>\u201cMy perspective is that will grow in the future to larger numbers. It will grow disproportionately\u201d</p>\n</blockquote>\n<p>I had the chance to be on John Gruber\u2019s show (recorded yesterday, guessing it\u2019ll drop today or tomorrow, assuming John is not too horrified with the results), and we were talking about this, peripherally. John mentioned the future possibility of Apple Prime, a concept similar to Amazon Prime. From the article:</p>\n<blockquote>\n<p>Under the argument for an iPhone subscription, which some people call Apple Prime after the Amazon program of the same name, Apple would bundle hardware upgrades with services like iCloud storage or Apple TV+ content and hardware for a single monthly fee.</p>\n</blockquote>\n<p>I suspect we\u2019ll all eventually be subscribing from a menu of services, including column A, software, column B, traditional services, and column C, hardware. Intriguing.</p>\n<p><a rel=\"bookmark\" href=\"https://www.loopinsight.com/2019/10/31/apple-is-laying-the-groundwork-for-an-iphone-subscription/\" title=\"Permanent link to 'Apple is laying the groundwork for an iPhone subscription'\">\u221e Read this on The Loop</a></p>"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/fhKs50EFS0SJPjSjfCR7lXwcMfs=/0x0:2040x1360/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59667903/acastro_180508_1777_google_IO_0002.0.jpg",
"width": 1310,
"height": 873,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 20737,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572578952687
},
{
"keywords": [
"Apple"
],
"originId": "https://9to5mac.com/?p=617610",
"fingerprint": "48affe9c",
"id": "BmoAzSEWHFzR01wyxBZAhNEo11Vy8oDR1qKDe+tKVEQ=_16e1de23321:1c6e5:d4506071",
"author": "Chance Miller",
"summary": {
"direction": "ltr",
"content": "<div><img src=\"https://9to5mac.com/wp-content/uploads/sites/6/2019/10/SCREEN-SHOT-2019-10-30-AT-12.55.55-PM.jpeg?quality=82&strip=all&w=1600\"></div>\n<p>Microsoft is planning several updates to its Outlook app for iPhone and iPad. As explained by <a href=\"https://www.theverge.com/2019/10/30/20940228/outlook-ios-mobile-ipados-split-view-do-not-disturb-smart-folders-features\"><em>The Verge</em></a>, Microsoft will roll out features such as iPad Split View, smart folders, and more to the Outlook app over the coming weeks.</p>\n<p> <a href=\"https://9to5mac.com/2019/10/30/microsoft-outlook-split-view-more/#more-617610\">more\u2026</a></p>\n<p>The post <a rel=\"nofollow\" href=\"https://9to5mac.com/2019/10/30/microsoft-outlook-split-view-more/\">Microsoft Outlook for iPhone and iPad adding Split View, Do Not Disturb, more</a> appeared first on <a rel=\"nofollow\" href=\"https://9to5mac.com\">9to5Mac</a>.</p>"
},
"alternate": [
{
"href": "https://9to5mac.com/2019/10/30/microsoft-outlook-split-view-more/",
"type": "text/html"
}
],
"crawled": 1572459393825,
"title": "Microsoft Outlook for iPhone and iPad adding Split View, Do Not Disturb, more",
"published": 1572458509000,
"origin": {
"streamId": "feed/http://9to5mac.com/feed/",
"htmlUrl": "https://9to5mac.com",
"title": "9to5Mac"
},
"unread": false,
"readTime": 13858,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572578905515
},
{
"keywords": [
"Miscellaneous"
],
"originId": "https://nshipster.com/device-identifiers",
"fingerprint": "5e66b947",
"id": "08l+9ftpGejQ9f/2DZ6dom5rSnNJJO9OCox6I3nUnWg=_16e22ccc11a:1e495:d4506071",
"updated": 1572505200000,
"author": "Mattt",
"summary": {
"direction": "ltr",
"content": "<p>For every era, there\u2019s a monster that embodies the anxieties of the age.</p>"
},
"alternate": [
{
"href": "https://nshipster.com/device-identifiers/",
"type": "text/html"
}
],
"crawled": 1572541874458,
"title": "Device Identifiers and Fingerprinting on iOS",
"published": 1572505200000,
"origin": {
"streamId": "feed/http://nshipster.com/feed.xml",
"htmlUrl": "https://nshipster.com/",
"title": "NSHipster"
},
"content": {
"direction": "ltr",
"content": "<p>For every era,\nthere\u2019s a monster that embodies the anxieties of the age.</p>\n<p>At the dawn of the <a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Holocene\">Holocene</a>,\nour ancestors traced the contours of shadows cast by the campfire\nas they kept watch over the darkness.\nOnce we learned to read the night sky for navigation,\nsailors swapped stories of sea creatures like\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Leviathan\">Leviathan</a> and\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Siren_%28mythology%29\">Siren</a>\nto describe the dangers of open ocean\n(and the perils to be found on unfamiliar shores).</p>\n<p><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Frankenstein%27s_monster\">Frankenstein\u2019s monster</a>\nwas as much the creation of Mary Shelley\nas it was a spiritual collaboration with\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Luigi_Galvani\">Luigi Galvani</a>.\nAnd Bram Stoker\u2019s\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Jewel_of_Seven_Stars\">fictionalized account of the mummy\u2019s curse</a>\nwas more a response to the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Egyptomania\">Egyptomania</a>\nand European colonialism\nof the nineteenth century\nthan any personal account of the Middle Kingdom.</p>\n<p>More recently,\nthe <a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Beach_Girls_and_the_Monster\">\u201cmonster ruins a beach party\u201d</a>\ntrope of the 1960s\narose from concerns of teenager morality.\nWhile the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Day_Mars_Invaded_Earth\">Martians</a>\nwho invaded those same drive-in double features\nserved as a proxy for Cold War fears at the height of the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Space_Race\">Space Race</a>.</p>\n<hr>\n<p>All of which begs the question:\n<em>\u201cWhat monster best exemplifies our present age?\u201d</em></p>\n<p>Consider the unnamed monster from the film\n<em><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/It_Follows\">It Follows</a></em>:\na formless, supernatural being that relentlessly pursues its victims\nanywhere on the planet.</p>\n<p>Sounds a bit like the state of\n<dfn title=\"advertising technology\">ad tech</dfn>\nin 2019, no?</p>\n<aside>\n<p>Setting aside its central theme of carnal karma,\nwhich follows the same well-trodden path of horror as our aforementioned beach monsters\u2026</p>\n</aside>\n<hr>\n<p>This week on NSHipster \u2014\nin celebration of our favorite holiday\n<abbr title=\"(Halloween)\">\ud83c\udf83</abbr> \u2014\nwe\u2019re taking a look at the myriad ways that\nyou\u2019re being tracked on iOS,\nboth sanctioned and unsanctioned,\nhistorically and presently.\nSo gather around the campfire,\nand allow us to trace the contours of the unseen, formless monsters\nthat stalk us under cover of <a href=\"https://nshipster.com/dark-mode/\">Dark Mode</a>.</p>\n<hr>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#the-cynicism-of-marketing-and-advertising-technology\"></a>The Cynicism of Marketing and Advertising Technology</h2>\n<p>Contrary to our intuitions about natural selection in the marketplace,\nhistory is littered with examples of\ninferior-but-better-marketed products winning out over superior alternatives:\n<em>VHS vs. Betamax</em>,\n<em>Windows vs. Macintosh</em>,\netc.\n(According to the common wisdom of business folks, at least.)\nRegardless,\nmost companies reach a point where\n<em>\u201cif you build it, they will come\u201d</em>\nceases to be a politically viable strategy,\nand someone authorizes a marketing budget.</p>\n<p>Marketers are tasked with growing market share\nby identifying and communicating with as many potential customers as possible.\nAnd many \u2014\neither out of a genuine belief or formulated as a post hoc rationalization \u2014\ntake the potential benefit of their product\nas a license to flouting long-established customs of personal privacy.\nSo they enlist the help of one or more\nadvertising firms,\nwho promise to maximize their allocated budget and\nprovide some accountability for their spending\nby using technology to\n<dfn><strong>target</strong></dfn>,\n<dfn><strong>deliver</strong></dfn>, and\n<dfn><strong>analyze</strong></dfn>\nmessaging to consumers.</p>\n<p><strong>Each of these tasks is predicated on a consistent identity,\nwhich is why advertisers go to such great lengths to track you.</strong></p>\n<ul>\n<li>Without knowing who you are,\nmarketers have no way to tell if you\u2019re a likely or even potential customer.</li>\n<li>Without knowing where you are,\nmarketers have no way to reach you\nother than to post ads where they\u2019re likely to be seen.</li>\n<li>Without knowing what you do,\nmarketers have no way to tell if their ads worked\nor were seen at all.</li>\n</ul>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#apple-sanctioned-identifiers\"></a>Apple-Sanctioned Identifiers</h2>\n<p>Apple\u2019s provided various APIS to facilitate user identification\nfor various purposes:</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#universal-identifiers-udid\"></a>Universal Identifiers (UDID)</h3>\n<p>In the early days of iOS,\nApple provided a <code>unique<wbr>Identifier</code> property on <code>UIDevice</code> \u2014\naffectionately referred to as a\n<abbr title=\"Universal Device Identifier\">UDID</abbr>\n(<a href=\"https://nshipster.com/uuid-udid-unique-identifier/\">not to be confused with a UUID</a>).\nAlthough such functionality seems unthinkable today,\nthat property existed until iOS 5,\nuntil it was\ndeprecated and replaced by <code>identifier<wbr>For<wbr>Vendor</code> in iOS 6.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#vendor-identifiers-idfv\"></a>Vendor Identifiers (IDFV)</h3>\n<p>Starting in iOS 6,\ndevelopers can use the\n<code>identifier<wbr>For<wbr>Vendor</code> property on <code>UIDevice</code>\nto generate a unique identifier that\u2019s shared across apps and extensions\ncreated by the same vendor\n(<abbr title=\"Identifier for Vendor\">IDFV</abbr>).</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>let</span> <span>idfv</span> <span>=</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>identifier<wbr>For<wbr>Vendor</span> <span>// BD43813E-CFC5-4EEB-ABE2-94562A6E76CA</span>\n </code></pre>\n<aside>\n<p>According to <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor\">the documentation</a>\n<code>identifier<wbr>For<wbr>Vendor</code> return <code>nil</code>\n\u201cafter the device has been restarted but before the user has unlocked the device.\u201d\nIt\u2019s unclear when that would be the case,\nbut something to keep in mind if your app does anything in the background.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#advertising-identifiers-idfa\"></a>Advertising Identifiers (IDFA)</h3>\n<p>Along with <code>identifier<wbr>For<wbr>Vendor</code> came the introduction of a new\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport\">AdSupport framework</a>,\nwhich Apple created to help distinguish\nidentification necessary for app functionality\nfrom anything in the service of advertising.</p>\n<p>The resulting\n<code>advertisingidentifier</code> property\n(affectionately referred to as\n<abbr title=\"Identifier for Advertisers\">IDFA</abbr> by its associates)\ndiffers from <code>identifier<wbr>For<wbr>Vendor</code>\nby returning the same value for everyone.\nThe value can change, for example,\nif the user <a rel=\"noopener noreferrer\" href=\"https://support.apple.com/en-us/HT205223\">resets their Advertising Identifier</a>\nor erases their device.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Ad<wbr>Support</span>\n <span>let</span> <span>idfa</span> <span>=</span> <span>ASIdentifier<wbr>Manager</span><span>.</span><span>shared</span><span>()</span><span>.</span><span>advertising<wbr>Identifier</span>\n </code></pre>\n<p>If advertising tracking is limited,\nthe property returns a zeroed-out UUID instead.</p>\n<pre data-lang=\"Swift\"><code><span>idfa</span><span>.</span><span>uuid<wbr>String</span> <span>==</span> <span>&quot;00000000-0000-0000-0000-000000000000&quot;</span> <span>// true if the user has limited ad tracking</span>\n </code></pre>\n<aside>\n<p>Curiously,\nmacOS Mojave introduced a\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport/asidentifiermanager/2998811-clearadvertisingidentifier\"><code>clear<wbr>Advertising<wbr>Identifier()</code> method</a>,\nwhich appears to create a <em>\u201ctragedy of the commons\u201d</em> situation,\nwhere a single app could spoil things for everyone else\n<em>(not that this is a bad thing from the user\u2019s perspective!)</em></p>\n</aside>\n<aside>\n<p>There\u2019s also the curious case of the\n<code>is<wbr>Advertising<wbr>Tracking<wbr>Enabled</code> property.\nAccording to\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier\">the documentation</a>:</p>\n<blockquote>\n<p>Check the value of this property\nbefore performing any advertising tracking.\nIf the value is false,\nuse the advertising identifier only for the following purposes:\nfrequency capping,\nattribution,\nconversion events,\nestimating the number of unique users,\nadvertising fraud detection,\nand debugging.</p>\n</blockquote>\n<p>This kind of <em>\u201chonor system\u201d</em> approach compliance is confusing.\nAnd it makes you wonder what kind of usage\n<em>wouldn\u2019t</em> fall within these broad allowances.</p>\n<p>If you have any insight into how this is policed,\n<a rel=\"noopener noreferrer\" href=\"https://twitter.com/nshipster\">drop us a line</a> \u2014\nwe\u2019d love to hear more.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#devicecheck\"></a>DeviceCheck</h3>\n<p><code>identifier<wbr>For<wbr>Vendor</code> and <code>advertising<wbr>Identifier</code>\nprovide all the same functionality as the <code>unique<wbr>Identifier</code> property\nthey replaced in iOS 6,\nsave for one:\nthe ability to persist across device resets and app uninstalls.</p>\n<p>In iOS 11,\nApple quietly introduced the\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/devicecheck\">DeviceCheck framework</a>,\nwhich allows developers to assign two bits of information\nthat are persisted by Apple\n<strong>until the developer manually removes them</strong>.</p>\n<p>Interacting with the DeviceCheck framework should be familiar to\nanyone familiar with <a href=\"https://nshipster.com/apns-device-tokens\">APNS</a>:\nafter setting things up on App Store Connect and your servers,\nthe client generates tokens on the device,\nwhich are sent to your servers to set or query two bits of information:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Device<wbr>Check</span>\n <span>let</span> <span>device</span> <span>=</span> <span>DCDevice</span><span>.</span><span>current</span>\n <span>if</span> <span>device</span><span>.</span><span>is<wbr>Supported</span> <span>{</span>\n <span>device</span><span>.</span><span>generate<wbr>Token</span> <span>{</span> <span>data</span><span>,</span> <span>error</span> <span>in</span>\n <span>if</span> <span>let</span> <span>token</span> <span>=</span> <span>data</span><span>?</span><span>.</span><span>base64Encoded<wbr>String</span><span>()</span> <span>{</span>\n <var>send token to your server</var>\n <span>}</span>\n <span>}</span>\n <span>}</span>\n </code></pre>\n<p>Based on the device token and other information sent by the client,\nthe server tells Apple to set each bit value\nby sending a JSON payload like this:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;device_token&quot;</span><span>:</span><span> </span><span>&quot;QTk4Qk<wbr>FDNEIt<wbr>NTBDMy00Qjc5LTh<wbr>BRUEt<wbr>MDQ5RTQz<wbr>Rj<wbr>NGQz<wbr>U0Cg==&quot;</span><span>,</span><span>\n</span><span>&quot;transaction_id&quot;</span><span>:</span><span> </span><span>&quot;D98BA630-E225-4A2F-AFEC-BE3A3D591708&quot;</span><span>,</span><span>\n</span><span>&quot;timestamp&quot;</span><span>:</span><span> </span><span>1572531720</span><span>,</span><span>\n</span><span>&quot;bit0&quot;</span><span>:</span><span> </span><span>true</span><span>,</span><span>\n</span><span>&quot;bit1&quot;</span><span>:</span><span> </span><span>false</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<p>To retrieve those two bits at a later point in time,\nthe server sends a payload without <code>bit0</code> and <code>bit1</code> fields:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;device_token&quot;</span><span>:</span><span> </span><span>&quot;QTk4Qk<wbr>FDNEIt<wbr>NTBDMy00Qjc5LTh<wbr>BRUEt<wbr>MDQ5RTQz<wbr>Rj<wbr>NGQz<wbr>U0Cg==&quot;</span><span>,</span><span>\n</span><span>&quot;transaction_id&quot;</span><span>:</span><span> </span><span>&quot;D98BA630-E225-4A2F-AFEC-BE3A3D591708&quot;</span><span>,</span><span>\n</span><span>&quot;timestamp&quot;</span><span>:</span><span> </span><span>1572532500</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<p>If everything worked,\nApple\u2019s servers would respond with a <code>200</code> status code\nand the following JSON payload:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;bit0&quot;</span><span> </span><span>:</span><span> </span><span>true</span><span>\n</span><span>&quot;bit1&quot;</span><span> </span><span>:</span><span> </span><span>false</span><span>,</span><span>\n</span><span>&quot;last_update_time&quot;</span><span> </span><span>:</span><span> </span><span>&quot;2019-10&quot;</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<aside>\n<p>Apple allegedly created the DeviceCheck framework\nto meet the needs of Uber\nin limiting the abuse of promotional codes.\nAlthough DeviceCheck proports to store <em>only</em> two bits of information\n(just enough to, for example,\ndetermine whether a device has ever been used to create an account\nand whether the device was ever associated with fraudulent activity),\nwe have (admittedly vague) concerns that the timestamp, even with truncation,\ncould be exploited to store more than two bits of information.</p>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#fingerprinting-in-todays-ios\"></a>Fingerprinting in Today\u2019s iOS</h2>\n<p>Despite these affordances by Apple,\nadvertisers have continued to work to circumvent user privacy protections\nand use any and all information at their disposal\nto identify users by other means.</p>\n<p>Over the years,\nApple\u2019s restricted access to information about\ndevice hardware,\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl\">installed apps</a>,\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_wifi-info\">nearby WiFi networks</a>.\nThey\u2019ve required apps to request permission to\nget your current location,\naccess your camera and microphone,\nflip through your contacts, and\nfind and connect to Bluetooth accessories.\nThey\u2019ve taken bold steps to <a rel=\"noopener noreferrer\" href=\"https://webkit.org/blog/8828/intelligent-tracking-prevention-2-2/\">prevent user tracking in Safari</a>.</p>\n<p>For lack of this information,\ncompanies have had to get creative,\nlooking to forge unique identities from the scraps of what\u2019s still available.\nThis process of identification by a combination of external factors\nis known as <dfn><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\">fingerprinting</a></dfn>.</p>\n<p>The unfortunate reality is that we can be uniquely identified\nby vanishingly small amounts of information.\nFor example,\nindividuals within a population can be singled out by as few as\nfour timestamped coordinates\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#de_montjoye_2013\">(de Montjoye, Hidalgo, Verleysen, &amp; Blondel, 2013)</a>\nor little more than a birthday and a ZIP code\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#sweeney_2000\">(Sweeney, 2000)</a>.</p>\n<p>Every WWDC since 2012 has featured a session about Privacy,\nbut the only mention of fingerprinting specifically was\n<a rel=\"noopener noreferrer\" href=\"https://asciiwwdc.com/2014/sessions/715?q=fingerprinting\">a brief discussion in 2014</a>\nabout how to avoid doing it.</p>\n<p>By our count,\na determined party could use conventional, unrestricted APIs\nto generate a few dozen bits of randomness:</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#locale-information-36-bits\"></a>Locale Information (~36 bits)</h3>\n<p>Locale information is the greatest source of identifying information\non Apple platforms.\nThe combination of your\npreferred languages, region, calendar, time zone,\nand which keyboards you have installed\nsay a lot about who you are \u2014\nespecially if you have less conventional preferences.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Foundation</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>language<wbr>Code</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Locale</span><span>.</span><span>iso<wbr>Language<wbr>Codes</span><span>.</span><span>count</span><span>))</span> <span>// 9.217 bits</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>region<wbr>Code</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Locale</span><span>.</span><span>iso<wbr>Region<wbr>Codes</span><span>.</span><span>count</span><span>))</span> <span>// 8 bits</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>calendar</span><span>.</span><span>identifier</span>\n <span>// ~2^4 (16) Calendars</span>\n <span>Time<wbr>Zone</span><span>.</span><span>current</span><span>.</span><span>identifier</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Time<wbr>Zone</span><span>.</span><span>known<wbr>Time<wbr>Zone<wbr>Identifiers</span><span>.</span><span>count</span><span>))</span> <span>// 8.775 bits</span>\n <span>User<wbr>Defaults</span><span>.</span><span>standard</span><span>.</span><span>object</span><span>(</span><span>for<wbr>Key</span><span>:</span> <span>&quot;Apple<wbr>Keyboards&quot;</span><span>)</span>\n <span>// ~2^6 (64) i<wbr>OS keyboards</span>\n </code></pre>\n<aside>\n<p>We recently <a rel=\"noopener noreferrer\" href=\"https://twitter.com/mattt/status/1175817188646612992\">Tweeted</a>\nabout apps having unrestricted access to emoji keyboard information.\nWe\u2019ve since been informed that Apple is investigating the issue.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#accessibility-10-bits\"></a>Accessibility (~10 bits)</h3>\n<p>Accessibility preferences also provide a great deal of information,\nwith each individual setting contributing a single potential bit:</p>\n<pre data-lang=\"Swift\"><code><span>UIAccessibility</span><span>.</span><span>is<wbr>Bold<wbr>Text<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Shake<wbr>To<wbr>Undo<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Reduce<wbr>Motion<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Darker<wbr>System<wbr>Colors<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Reduce<wbr>Transparency<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Assistive<wbr>Touch<wbr>Running</span>\n </code></pre>\n<p>Of the approximately ~25% of users who take advantage of\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically\">Dynamic Type</a>\nby configuring a preferred font size,\nthat selection may also be used to fingerprint you:</p>\n<pre data-lang=\"Swift\"><code><span>let</span> <span>application</span> <span>=</span> <span>UIApplication</span><span>.</span><span>shared</span>\n <span>application</span><span>.</span><span>preferred<wbr>Content<wbr>Size<wbr>Category</span>\n </code></pre>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#hardware-information-5-or-6-bits\"></a>Hardware Information (~5 or ~6 bits)</h3>\n<p>Although most of the juiciest bits have been locked down\nin OS updates over the years,\nthere\u2019s just enough to contribute a few more bits for purposes of identification.</p>\n<p>On iOS,\nyou can get the current model and amount of storage of a user\u2019s device:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>let</span> <span>device</span> <span>=</span> <span>UIDevice</span><span>.</span><span>current</span>\n <span>device</span><span>.</span><span>name</span> <span>// &quot;i<wbr>Phone 11 Pro&quot;</span>\n <span>let</span> <span>file<wbr>Manager</span> <span>=</span> <span>File<wbr>Manager</span><span>.</span><span>default</span>\n <span>if</span> <span>let</span> <span>path</span> <span>=</span> <span>file<wbr>Manager</span><span>.</span><span>urls</span><span>(</span><span>for</span><span>:</span> <span>.</span><span>library<wbr>Directory</span><span>,</span> <span>in</span><span>:</span> <span>.</span><span>system<wbr>Domain<wbr>Mask</span><span>)</span><span>.</span><span>last</span><span>?</span><span>.</span><span>path</span><span>,</span>\n <span>let</span> <span>system<wbr>Size</span> <span>=</span> <span>try</span><span>?</span> <span>file<wbr>Manager</span><span>.</span><span>attributes<wbr>Of<wbr>File<wbr>System</span><span>(</span><span>for<wbr>Path</span><span>:</span> <span>path</span><span>)[</span><span>.</span><span>system<wbr>Size</span><span>]</span> <span>as?</span> <span>Int</span>\n <span>{</span>\n <span>Measurement</span><span>&lt;</span><span>Unit<wbr>Information<wbr>Storage</span><span>></span><span>(</span><span>value</span><span>:</span> <span>Double</span><span>(</span><span>system<wbr>Size</span><span>),</span> <span>unit</span><span>:</span> <span>.</span><span>bytes</span><span>)</span>\n <span>.</span><span>converted</span><span>(</span><span>to</span><span>:</span> <span>.</span><span>gigabytes</span><span>)</span> <span>// ~256GB</span>\n <span>}</span>\n </code></pre>\n<p>With <a rel=\"noopener noreferrer\" href=\"https://support.apple.com/guide/iphone/supported-models-iphe3fa5df43/ios\">14 supported iOS devices</a>,\nmost having 3 configurations each,\nlet\u2019s say that this contributes about 32 possibilities, or 5 bits.</p>\n<p>You can go a few steps further on macOS,\nto further differentiate hardware by its processor count and amount of RAM:</p>\n<pre data-lang=\"Swift\"><code><span>process<wbr>Info</span><span>.</span><span>processor<wbr>Count</span> <span>// 8</span>\n <span>Measurement</span><span>&lt;</span><span>Unit<wbr>Information<wbr>Storage</span><span>></span><span>(</span><span>value</span><span>:</span> <span>Double</span><span>(</span><span>process<wbr>Info</span><span>.</span><span>physical<wbr>Memory</span><span>),</span>\n <span>unit</span><span>:</span> <span>.</span><span>bytes</span><span>)</span>\n <span>.</span><span>converted</span><span>(</span><span>to</span><span>:</span> <span>.</span><span>gigabytes</span><span>)</span> <span>// 16GB</span>\n </code></pre>\n<p>It\u2019s hard to get a sense of\n<a rel=\"noopener noreferrer\" href=\"https://everymac.com/systems/by_capability/minimum-macos-supported.html\">how many different Mac models are in use</a>,\nbut a reasonable estimate would be on the order of 2<sup>6</sup> or 2<sup>7</sup>.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#cellular-network-2-bits\"></a>Cellular Network (~2 bits)</h3>\n<p>Knowing whether someone\u2019s phone is on Verizon or Vodaphone\ncan also be factored into a fingerprint.\nYou can use the <code>CTTelephony<wbr>Network<wbr>Info</code> class from the\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/coretelephony\">CoreTelephony framework</a>\nto lookup the providers for devices with cellular service:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Core<wbr>Telephony</span>\n <span>let</span> <span>network<wbr>Info</span> <span>=</span> <span>CTTelephony<wbr>Network<wbr>Info</span><span>()</span>\n <span>let</span> <span>carriers</span> <span>=</span> <span>network<wbr>Info</span><span>.</span><span>service<wbr>Subscriber<wbr>Cellular<wbr>Providers</span><span>?</span><span>.</span><span>values</span>\n <span>carriers</span><span>?</span><span>.</span><span>map</span> <span>{</span> <span>(</span><span>$0</span><span>.</span><span>mobile<wbr>Network<wbr>Code</span><span>,</span> <span>$0</span><span>.</span><span>mobile<wbr>Country<wbr>Code</span><span>)</span> <span>}</span>\n </code></pre>\n<p>The number of providers varies per country,\nbut using the 4 major carriers in United States\nas a guideline,\nwe can say carrier information would contribute about 2 bits\n(or more if you have multiple SIM cards installed).</p>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#communication-preferences-2-bits\"></a>Communication Preferences (2 bits)</h2>\n<p>More generally,\neven knowing whether someone can send texts or email at all\ncan be factored into a fingerprint.\nThis information can be gathered without permissions via\nthe <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/messageui\">MessageUI framework</a>.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Message<wbr>UI</span>\n <span>MFMail<wbr>Compose<wbr>View<wbr>Controller</span><span>.</span><span>can<wbr>Send<wbr>Mail</span><span>()</span>\n <span>MFMessage<wbr>Compose<wbr>View<wbr>Controller</span><span>.</span><span>can<wbr>Send<wbr>Text</span><span>()</span>\n </code></pre>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#additional-sources-of-identifying-information\"></a>Additional Sources of Identifying Information</h2>\n<p>If the use of digital fingerprinting seems outlandish,\nthat\u2019s just scratching the surface of how companies and researchers\nhave figured out how to circumvent your privacy.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#geoip-and-relative-network-speeds\"></a>GeoIP and Relative Network Speeds</h3>\n<p>Although access to geolocation through conventional APIs\nrequires explicit authorization,\nthird parties may be able to get a general sense of where you are in the world\nbased on how you access the Internet.</p>\n<p><a rel=\"noopener noreferrer\" href=\"https://ipinfo.io\">Geolocation by source IP address</a>\nis used extensively for things like region locking and localization.\nYou could also combine this information with\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/library/archive/samplecode/SimplePing/Introduction/Intro.html#//apple_ref/doc/uid/DTS10000716\">ping-time measurements</a>\nto hosts in known locations\nto get a more accurate pinpoint on location <a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#weinberg_2018\">(Weinberg, Cho, Christin, Sekar, &amp; Gill, 2018)</a>:</p>\n<pre data-lang=\"Terminal\"><code><span>ping -c 5 99.24.18.13 #</span> San Francisco, USA\n <span>\n--- 99.24.18.13 ping statistics ---\n5 packets transmitted, 5 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 11.900/12.184/12.895/0.380 ms\n</span><span>ping -c 5 203.47.10.37 #</span> Adelaide, Australia\n <span>\n--- 203.47.10.37 ping statistics ---\n5 packets transmitted, 5 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 202.122/202.433/203.436/0.504 ms\n</span></code></pre>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#battery-health\"></a>Battery Health</h3>\n<p>It\u2019s unclear whether this is a concern in iOS,\nbut depending on how precise the results of <code>UIDevice</code> battery APIs are,\nyou may be able to use them to identify a device by its battery health.\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#olejnik_2015\">(Olejnik, Acar, Castelluccia, &amp; Diaz, 2016)</a></p>\n<pre data-lang=\"Swift\"><code><span>var</span> <span>timestamped<wbr>Battery<wbr>Levels</span><span>:</span> <span>[(</span><span>Date</span><span>,</span> <span>Float</span><span>)]</span> <span>=</span> <span>[]</span>\n <span>if</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>is<wbr>Battery<wbr>Monitoring<wbr>Enabled</span> <span>{</span>\n <span>timestamped<wbr>Battery<wbr>Levels</span><span>.</span><span>append</span><span>((</span><span>Date</span><span>(),</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>battery<wbr>Level</span><span>))</span>\n <span>}</span>\n </code></pre>\n<aside>\n<p>For this reason,\nbattery level APIs were\n<a rel=\"noopener noreferrer\" href=\"https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API\">removed in Firefox 55</a>.</p>\n<p>If this seems outlandish,\nconsider that Apple recently released a security update for iOS after researchers\ndemonstrated that small discrepancies in gyroscope calibration settings\ncould be used to uniquely identify devices.\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#zhang_2019\">(Zhang, Beresford, &amp; Sheret, 2019)</a></p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#and-so-on\"></a>And so on\u2026</h3>\n<p>Everything from your heartbeat, to your gait, to your\n<a rel=\"noopener noreferrer\" href=\"https://www.wired.co.uk/article/surveillance-technology-biometrics\">butt shape</a>\nseem capable of leaking your identity.\nIt can all be quite overwhelming.</p>\n<p>I mean,\nif a motivated individual can find your home address by\n<a rel=\"noopener noreferrer\" href=\"https://www.theverge.com/2019/10/11/20910551/stalker-attacked-pop-idol-reflection-pupils-selfies-videos-photos-google-street-view-japan\">cross-referencing the reflection in your eyes against Google Street view</a>,\nhow can we even stand a chance out there?</p>\n<hr>\n<p>Much as we may bemoan the current duopoly of mobile operating systems,\nwe might take some solace in the fact that\nat least one of the players actually cares about user privacy.\nThough it\u2019s unclear whether that\u2019s a fight that can ever be won.</p>\n<p>At times,\nour fate of being tracked and advertised to\nmay seem as inevitable as the victims in <em>It Follows</em>.</p>\n<p>But let\u2019s not forget that,\nas technologists, as people with a voice,\nwe\u2019re in a position to fight back.</p>\n<footer>\n<h5>References</h5>\n<ol>\n<li><span>de Montjoye, Y.-A., Hidalgo, C. A., Verleysen, M., &amp; Blondel, V. D. (2013). Unique in the Crowd: The privacy bounds of human mobility. <i>Scientific Reports</i>, <i>3</i>, 1376. https://doi.org/10.1038/srep01376</span></li>\n<li><span>Sweeney, L. (2000). Simple Demographics Often Identify People Uniquely. <i>Carnegie Mellon University, Data Privacy</i>. Working paper. Retrieved from http://dataprivacylab.org/projects/identifiability/</span></li>\n<li><span>Weinberg, Z., Cho, S., Christin, N., Sekar, V., &amp; Gill, P. (2018). How to Catch when Proxies Lie: Verifying the Physical Locations of Network Proxies with Active Geolocation. In <i>Proceedings of the Internet Measurement Conference 2018</i> (pp. 203\u2013217). New York, NY, USA: ACM. https://doi.org/10.1145/3278532.3278551</span></li>\n<li><span>Olejnik, \\L., Acar, G., Castelluccia, C., &amp; Diaz, C. (2016). The Leaking Battery. In <i>Revised Selected Papers of the 10th International Workshop on Data Privacy Management, and Security Assurance - Volume 9481</i> (pp. 254\u2013263). New York, NY, USA: Springer-Verlag New York, Inc. https://doi.org/10.1007/978-3-319-29883-2_18</span></li>\n<li><span>Zhang, J., Beresford, A. R., &amp; Sheret, I. (2019). SensorID: Sensor Calibration Fingerprinting for Smartphones. In <i>Proceedings of the 40th IEEE Symposium on Security and Privacy (SP)</i>. IEEE.</span></li>\n</ol>\n</footer>"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/BkK9uk46u1ujrjGO0Jav3PCQ3g0=/0x225:5363x3800/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59608177/PIA22227_full.0.jpg",
"width": 1310,
"height": 873,
"contentType": "image/jpeg"
},
"unread": true,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/885f2e01-d314-4e63-abac-17dcb063f5b5",
"label": "Programming"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572578876975
},
{
"keywords": [
"Miscellaneous"
],
"originId": "https://nshipster.com/device-identifiers",
"fingerprint": "2f52765d",
"id": "CmHb1hXBWguYpGAhzgwJM9xvPVSYJFbt7KLqF3nqYQ0=_16e22eb0ef0:1e551:d4506071",
"updated": 1572505200000,
"author": "Mattt",
"summary": {
"direction": "ltr",
"content": "<p>For every era, there\u2019s a monster that embodies the anxieties of the age.</p>"
},
"alternate": [
{
"href": "http://feedproxy.google.com/~r/NSHipster/~3/hXb7k2avhFE/",
"type": "text/html"
}
],
"canonical": [
{
"href": "https://nshipster.com/device-identifiers/",
"type": "text/html"
}
],
"crawled": 1572543860464,
"title": "Device Identifiers and Fingerprinting on iOS",
"published": 1572505200000,
"origin": {
"streamId": "feed/http://feeds.feedburner.com/NSHipster",
"htmlUrl": "https://nshipster.com/",
"title": "NSHipster"
},
"content": {
"direction": "ltr",
"content": "<p>For every era,\nthere\u2019s a monster that embodies the anxieties of the age.</p>\n<p>At the dawn of the <a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Holocene\">Holocene</a>,\nour ancestors traced the contours of shadows cast by the campfire\nas they kept watch over the darkness.\nOnce we learned to read the night sky for navigation,\nsailors swapped stories of sea creatures like\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Leviathan\">Leviathan</a> and\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Siren_%28mythology%29\">Siren</a>\nto describe the dangers of open ocean\n(and the perils to be found on unfamiliar shores).</p>\n<p><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Frankenstein%27s_monster\">Frankenstein\u2019s monster</a>\nwas as much the creation of Mary Shelley\nas it was a spiritual collaboration with\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Luigi_Galvani\">Luigi Galvani</a>.\nAnd Bram Stoker\u2019s\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Jewel_of_Seven_Stars\">fictionalized account of the mummy\u2019s curse</a>\nwas more a response to the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Egyptomania\">Egyptomania</a>\nand European colonialism\nof the nineteenth century\nthan any personal account of the Middle Kingdom.</p>\n<p>More recently,\nthe <a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Beach_Girls_and_the_Monster\">\u201cmonster ruins a beach party\u201d</a>\ntrope of the 1960s\narose from concerns of teenager morality.\nWhile the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/The_Day_Mars_Invaded_Earth\">Martians</a>\nwho invaded those same drive-in double features\nserved as a proxy for Cold War fears at the height of the\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Space_Race\">Space Race</a>.</p>\n<hr>\n<p>All of which begs the question:\n<em>\u201cWhat monster best exemplifies our present age?\u201d</em></p>\n<p>Consider the unnamed monster from the film\n<em><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/It_Follows\">It Follows</a></em>:\na formless, supernatural being that relentlessly pursues its victims\nanywhere on the planet.</p>\n<p>Sounds a bit like the state of\n<dfn title=\"advertising technology\">ad tech</dfn>\nin 2019, no?</p>\n<aside>\n<p>Setting aside its central theme of carnal karma,\nwhich follows the same well-trodden path of horror as our aforementioned beach monsters\u2026</p>\n</aside>\n<hr>\n<p>This week on NSHipster \u2014\nin celebration of our favorite holiday\n<abbr title=\"(Halloween)\">\ud83c\udf83</abbr> \u2014\nwe\u2019re taking a look at the myriad ways that\nyou\u2019re being tracked on iOS,\nboth sanctioned and unsanctioned,\nhistorically and presently.\nSo gather around the campfire,\nand allow us to trace the contours of the unseen, formless monsters\nthat stalk us under cover of <a href=\"https://nshipster.com/dark-mode/\">Dark Mode</a>.</p>\n<hr>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#the-cynicism-of-marketing-and-advertising-technology\"></a>The Cynicism of Marketing and Advertising Technology</h2>\n<p>Contrary to our intuitions about natural selection in the marketplace,\nhistory is littered with examples of\ninferior-but-better-marketed products winning out over superior alternatives:\n<em>VHS vs. Betamax</em>,\n<em>Windows vs. Macintosh</em>,\netc.\n(According to the common wisdom of business folks, at least.)\nRegardless,\nmost companies reach a point where\n<em>\u201cif you build it, they will come\u201d</em>\nceases to be a politically viable strategy,\nand someone authorizes a marketing budget.</p>\n<p>Marketers are tasked with growing market share\nby identifying and communicating with as many potential customers as possible.\nAnd many \u2014\neither out of a genuine belief or formulated as a post hoc rationalization \u2014\ntake the potential benefit of their product\nas a license to flouting long-established customs of personal privacy.\nSo they enlist the help of one or more\nadvertising firms,\nwho promise to maximize their allocated budget and\nprovide some accountability for their spending\nby using technology to\n<dfn><strong>target</strong></dfn>,\n<dfn><strong>deliver</strong></dfn>, and\n<dfn><strong>analyze</strong></dfn>\nmessaging to consumers.</p>\n<p><strong>Each of these tasks is predicated on a consistent identity,\nwhich is why advertisers go to such great lengths to track you.</strong></p>\n<ul>\n<li>Without knowing who you are,\nmarketers have no way to tell if you\u2019re a likely or even potential customer.</li>\n<li>Without knowing where you are,\nmarketers have no way to reach you\nother than to post ads where they\u2019re likely to be seen.</li>\n<li>Without knowing what you do,\nmarketers have no way to tell if their ads worked\nor were seen at all.</li>\n</ul>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#apple-sanctioned-identifiers\"></a>Apple-Sanctioned Identifiers</h2>\n<p>Apple\u2019s provided various APIS to facilitate user identification\nfor various purposes:</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#universal-identifiers-udid\"></a>Universal Identifiers (UDID)</h3>\n<p>In the early days of iOS,\nApple provided a <code>unique<wbr>Identifier</code> property on <code>UIDevice</code> \u2014\naffectionately referred to as a\n<abbr title=\"Universal Device Identifier\">UDID</abbr>\n(<a href=\"https://nshipster.com/uuid-udid-unique-identifier/\">not to be confused with a UUID</a>).\nAlthough such functionality seems unthinkable today,\nthat property existed until iOS 5,\nuntil it was\ndeprecated and replaced by <code>identifier<wbr>For<wbr>Vendor</code> in iOS 6.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#vendor-identifiers-idfv\"></a>Vendor Identifiers (IDFV)</h3>\n<p>Starting in iOS 6,\ndevelopers can use the\n<code>identifier<wbr>For<wbr>Vendor</code> property on <code>UIDevice</code>\nto generate a unique identifier that\u2019s shared across apps and extensions\ncreated by the same vendor\n(<abbr title=\"Identifier for Vendor\">IDFV</abbr>).</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>let</span> <span>idfv</span> <span>=</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>identifier<wbr>For<wbr>Vendor</span> <span>// BD43813E-CFC5-4EEB-ABE2-94562A6E76CA</span>\n </code></pre>\n<aside>\n<p>According to <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor\">the documentation</a>\n<code>identifier<wbr>For<wbr>Vendor</code> return <code>nil</code>\n\u201cafter the device has been restarted but before the user has unlocked the device.\u201d\nIt\u2019s unclear when that would be the case,\nbut something to keep in mind if your app does anything in the background.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#advertising-identifiers-idfa\"></a>Advertising Identifiers (IDFA)</h3>\n<p>Along with <code>identifier<wbr>For<wbr>Vendor</code> came the introduction of a new\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport\">AdSupport framework</a>,\nwhich Apple created to help distinguish\nidentification necessary for app functionality\nfrom anything in the service of advertising.</p>\n<p>The resulting\n<code>advertisingidentifier</code> property\n(affectionately referred to as\n<abbr title=\"Identifier for Advertisers\">IDFA</abbr> by its associates)\ndiffers from <code>identifier<wbr>For<wbr>Vendor</code>\nby returning the same value for everyone.\nThe value can change, for example,\nif the user <a rel=\"noopener noreferrer\" href=\"https://support.apple.com/en-us/HT205223\">resets their Advertising Identifier</a>\nor erases their device.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Ad<wbr>Support</span>\n <span>let</span> <span>idfa</span> <span>=</span> <span>ASIdentifier<wbr>Manager</span><span>.</span><span>shared</span><span>()</span><span>.</span><span>advertising<wbr>Identifier</span>\n </code></pre>\n<p>If advertising tracking is limited,\nthe property returns a zeroed-out UUID instead.</p>\n<pre data-lang=\"Swift\"><code><span>idfa</span><span>.</span><span>uuid<wbr>String</span> <span>==</span> <span>&quot;00000000-0000-0000-0000-000000000000&quot;</span> <span>// true if the user has limited ad tracking</span>\n </code></pre>\n<aside>\n<p>Curiously,\nmacOS Mojave introduced a\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport/asidentifiermanager/2998811-clearadvertisingidentifier\"><code>clear<wbr>Advertising<wbr>Identifier()</code> method</a>,\nwhich appears to create a <em>\u201ctragedy of the commons\u201d</em> situation,\nwhere a single app could spoil things for everyone else\n<em>(not that this is a bad thing from the user\u2019s perspective!)</em></p>\n</aside>\n<aside>\n<p>There\u2019s also the curious case of the\n<code>is<wbr>Advertising<wbr>Tracking<wbr>Enabled</code> property.\nAccording to\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier\">the documentation</a>:</p>\n<blockquote>\n<p>Check the value of this property\nbefore performing any advertising tracking.\nIf the value is false,\nuse the advertising identifier only for the following purposes:\nfrequency capping,\nattribution,\nconversion events,\nestimating the number of unique users,\nadvertising fraud detection,\nand debugging.</p>\n</blockquote>\n<p>This kind of <em>\u201chonor system\u201d</em> approach compliance is confusing.\nAnd it makes you wonder what kind of usage\n<em>wouldn\u2019t</em> fall within these broad allowances.</p>\n<p>If you have any insight into how this is policed,\n<a rel=\"noopener noreferrer\" href=\"https://twitter.com/nshipster\">drop us a line</a> \u2014\nwe\u2019d love to hear more.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#devicecheck\"></a>DeviceCheck</h3>\n<p><code>identifier<wbr>For<wbr>Vendor</code> and <code>advertising<wbr>Identifier</code>\nprovide all the same functionality as the <code>unique<wbr>Identifier</code> property\nthey replaced in iOS 6,\nsave for one:\nthe ability to persist across device resets and app uninstalls.</p>\n<p>In iOS 11,\nApple quietly introduced the\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/devicecheck\">DeviceCheck framework</a>,\nwhich allows developers to assign two bits of information\nthat are persisted by Apple\n<strong>until the developer manually removes them</strong>.</p>\n<p>Interacting with the DeviceCheck framework should be familiar to\nanyone familiar with <a href=\"https://nshipster.com/apns-device-tokens\">APNS</a>:\nafter setting things up on App Store Connect and your servers,\nthe client generates tokens on the device,\nwhich are sent to your servers to set or query two bits of information:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Device<wbr>Check</span>\n <span>let</span> <span>device</span> <span>=</span> <span>DCDevice</span><span>.</span><span>current</span>\n <span>if</span> <span>device</span><span>.</span><span>is<wbr>Supported</span> <span>{</span>\n <span>device</span><span>.</span><span>generate<wbr>Token</span> <span>{</span> <span>data</span><span>,</span> <span>error</span> <span>in</span>\n <span>if</span> <span>let</span> <span>token</span> <span>=</span> <span>data</span><span>?</span><span>.</span><span>base64Encoded<wbr>String</span><span>()</span> <span>{</span>\n <var>send token to your server</var>\n <span>}</span>\n <span>}</span>\n <span>}</span>\n </code></pre>\n<p>Based on the device token and other information sent by the client,\nthe server tells Apple to set each bit value\nby sending a JSON payload like this:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;device_token&quot;</span><span>:</span><span> </span><span>&quot;QTk4Qk<wbr>FDNEIt<wbr>NTBDMy00Qjc5LTh<wbr>BRUEt<wbr>MDQ5RTQz<wbr>Rj<wbr>NGQz<wbr>U0Cg==&quot;</span><span>,</span><span>\n</span><span>&quot;transaction_id&quot;</span><span>:</span><span> </span><span>&quot;D98BA630-E225-4A2F-AFEC-BE3A3D591708&quot;</span><span>,</span><span>\n</span><span>&quot;timestamp&quot;</span><span>:</span><span> </span><span>1572531720</span><span>,</span><span>\n</span><span>&quot;bit0&quot;</span><span>:</span><span> </span><span>true</span><span>,</span><span>\n</span><span>&quot;bit1&quot;</span><span>:</span><span> </span><span>false</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<p>To retrieve those two bits at a later point in time,\nthe server sends a payload without <code>bit0</code> and <code>bit1</code> fields:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;device_token&quot;</span><span>:</span><span> </span><span>&quot;QTk4Qk<wbr>FDNEIt<wbr>NTBDMy00Qjc5LTh<wbr>BRUEt<wbr>MDQ5RTQz<wbr>Rj<wbr>NGQz<wbr>U0Cg==&quot;</span><span>,</span><span>\n</span><span>&quot;transaction_id&quot;</span><span>:</span><span> </span><span>&quot;D98BA630-E225-4A2F-AFEC-BE3A3D591708&quot;</span><span>,</span><span>\n</span><span>&quot;timestamp&quot;</span><span>:</span><span> </span><span>1572532500</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<p>If everything worked,\nApple\u2019s servers would respond with a <code>200</code> status code\nand the following JSON payload:</p>\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;bit0&quot;</span><span> </span><span>:</span><span> </span><span>true</span><span>\n</span><span>&quot;bit1&quot;</span><span> </span><span>:</span><span> </span><span>false</span><span>,</span><span>\n</span><span>&quot;last_update_time&quot;</span><span> </span><span>:</span><span> </span><span>&quot;2019-10&quot;</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n<aside>\n<p>Apple allegedly created the DeviceCheck framework\nto meet the needs of Uber\nin limiting the abuse of promotional codes.\nAlthough DeviceCheck proports to store <em>only</em> two bits of information\n(just enough to, for example,\ndetermine whether a device has ever been used to create an account\nand whether the device was ever associated with fraudulent activity),\nwe have (admittedly vague) concerns that the timestamp, even with truncation,\ncould be exploited to store more than two bits of information.</p>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#fingerprinting-in-todays-ios\"></a>Fingerprinting in Today\u2019s iOS</h2>\n<p>Despite these affordances by Apple,\nadvertisers have continued to work to circumvent user privacy protections\nand use any and all information at their disposal\nto identify users by other means.</p>\n<p>Over the years,\nApple\u2019s restricted access to information about\ndevice hardware,\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl\">installed apps</a>,\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_wifi-info\">nearby WiFi networks</a>.\nThey\u2019ve required apps to request permission to\nget your current location,\naccess your camera and microphone,\nflip through your contacts, and\nfind and connect to Bluetooth accessories.\nThey\u2019ve taken bold steps to <a rel=\"noopener noreferrer\" href=\"https://webkit.org/blog/8828/intelligent-tracking-prevention-2-2/\">prevent user tracking in Safari</a>.</p>\n<p>For lack of this information,\ncompanies have had to get creative,\nlooking to forge unique identities from the scraps of what\u2019s still available.\nThis process of identification by a combination of external factors\nis known as <dfn><a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Fingerprint_%28computing%29\">fingerprinting</a></dfn>.</p>\n<p>The unfortunate reality is that we can be uniquely identified\nby vanishingly small amounts of information.\nFor example,\nindividuals within a population can be singled out by as few as\nfour timestamped coordinates\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#de_montjoye_2013\">(de Montjoye, Hidalgo, Verleysen, &amp; Blondel, 2013)</a>\nor little more than a birthday and a ZIP code\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#sweeney_2000\">(Sweeney, 2000)</a>.</p>\n<p>Every WWDC since 2012 has featured a session about Privacy,\nbut the only mention of fingerprinting specifically was\n<a rel=\"noopener noreferrer\" href=\"https://asciiwwdc.com/2014/sessions/715?q=fingerprinting\">a brief discussion in 2014</a>\nabout how to avoid doing it.</p>\n<p>By our count,\na determined party could use conventional, unrestricted APIs\nto generate a few dozen bits of randomness:</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#locale-information-36-bits\"></a>Locale Information (~36 bits)</h3>\n<p>Locale information is the greatest source of identifying information\non Apple platforms.\nThe combination of your\npreferred languages, region, calendar, time zone,\nand which keyboards you have installed\nsay a lot about who you are \u2014\nespecially if you have less conventional preferences.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Foundation</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>language<wbr>Code</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Locale</span><span>.</span><span>iso<wbr>Language<wbr>Codes</span><span>.</span><span>count</span><span>))</span> <span>// 9.217 bits</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>region<wbr>Code</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Locale</span><span>.</span><span>iso<wbr>Region<wbr>Codes</span><span>.</span><span>count</span><span>))</span> <span>// 8 bits</span>\n <span>Locale</span><span>.</span><span>current</span><span>.</span><span>calendar</span><span>.</span><span>identifier</span>\n <span>// ~2^4 (16) Calendars</span>\n <span>Time<wbr>Zone</span><span>.</span><span>current</span><span>.</span><span>identifier</span>\n <span>log2</span><span>(</span><span>Double</span><span>(</span><span>Time<wbr>Zone</span><span>.</span><span>known<wbr>Time<wbr>Zone<wbr>Identifiers</span><span>.</span><span>count</span><span>))</span> <span>// 8.775 bits</span>\n <span>User<wbr>Defaults</span><span>.</span><span>standard</span><span>.</span><span>object</span><span>(</span><span>for<wbr>Key</span><span>:</span> <span>&quot;Apple<wbr>Keyboards&quot;</span><span>)</span>\n <span>// ~2^6 (64) i<wbr>OS keyboards</span>\n </code></pre>\n<aside>\n<p>We recently <a rel=\"noopener noreferrer\" href=\"https://twitter.com/mattt/status/1175817188646612992\">Tweeted</a>\nabout apps having unrestricted access to emoji keyboard information.\nWe\u2019ve since been informed that Apple is investigating the issue.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#accessibility-10-bits\"></a>Accessibility (~10 bits)</h3>\n<p>Accessibility preferences also provide a great deal of information,\nwith each individual setting contributing a single potential bit:</p>\n<pre data-lang=\"Swift\"><code><span>UIAccessibility</span><span>.</span><span>is<wbr>Bold<wbr>Text<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Shake<wbr>To<wbr>Undo<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Reduce<wbr>Motion<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Darker<wbr>System<wbr>Colors<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Reduce<wbr>Transparency<wbr>Enabled</span>\n <span>UIAccessibility</span><span>.</span><span>is<wbr>Assistive<wbr>Touch<wbr>Running</span>\n </code></pre>\n<p>Of the approximately ~25% of users who take advantage of\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically\">Dynamic Type</a>\nby configuring a preferred font size,\nthat selection may also be used to fingerprint you:</p>\n<pre data-lang=\"Swift\"><code><span>let</span> <span>application</span> <span>=</span> <span>UIApplication</span><span>.</span><span>shared</span>\n <span>application</span><span>.</span><span>preferred<wbr>Content<wbr>Size<wbr>Category</span>\n </code></pre>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#hardware-information-5-or-6-bits\"></a>Hardware Information (~5 or ~6 bits)</h3>\n<p>Although most of the juiciest bits have been locked down\nin OS updates over the years,\nthere\u2019s just enough to contribute a few more bits for purposes of identification.</p>\n<p>On iOS,\nyou can get the current model and amount of storage of a user\u2019s device:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>let</span> <span>device</span> <span>=</span> <span>UIDevice</span><span>.</span><span>current</span>\n <span>device</span><span>.</span><span>name</span> <span>// &quot;i<wbr>Phone 11 Pro&quot;</span>\n <span>let</span> <span>file<wbr>Manager</span> <span>=</span> <span>File<wbr>Manager</span><span>.</span><span>default</span>\n <span>if</span> <span>let</span> <span>path</span> <span>=</span> <span>file<wbr>Manager</span><span>.</span><span>urls</span><span>(</span><span>for</span><span>:</span> <span>.</span><span>library<wbr>Directory</span><span>,</span> <span>in</span><span>:</span> <span>.</span><span>system<wbr>Domain<wbr>Mask</span><span>)</span><span>.</span><span>last</span><span>?</span><span>.</span><span>path</span><span>,</span>\n <span>let</span> <span>system<wbr>Size</span> <span>=</span> <span>try</span><span>?</span> <span>file<wbr>Manager</span><span>.</span><span>attributes<wbr>Of<wbr>File<wbr>System</span><span>(</span><span>for<wbr>Path</span><span>:</span> <span>path</span><span>)[</span><span>.</span><span>system<wbr>Size</span><span>]</span> <span>as?</span> <span>Int</span>\n <span>{</span>\n <span>Measurement</span><span>&lt;</span><span>Unit<wbr>Information<wbr>Storage</span><span>></span><span>(</span><span>value</span><span>:</span> <span>Double</span><span>(</span><span>system<wbr>Size</span><span>),</span> <span>unit</span><span>:</span> <span>.</span><span>bytes</span><span>)</span>\n <span>.</span><span>converted</span><span>(</span><span>to</span><span>:</span> <span>.</span><span>gigabytes</span><span>)</span> <span>// ~256GB</span>\n <span>}</span>\n </code></pre>\n<p>With <a rel=\"noopener noreferrer\" href=\"https://support.apple.com/guide/iphone/supported-models-iphe3fa5df43/ios\">14 supported iOS devices</a>,\nmost having 3 configurations each,\nlet\u2019s say that this contributes about 32 possibilities, or 5 bits.</p>\n<p>You can go a few steps further on macOS,\nto further differentiate hardware by its processor count and amount of RAM:</p>\n<pre data-lang=\"Swift\"><code><span>process<wbr>Info</span><span>.</span><span>processor<wbr>Count</span> <span>// 8</span>\n <span>Measurement</span><span>&lt;</span><span>Unit<wbr>Information<wbr>Storage</span><span>></span><span>(</span><span>value</span><span>:</span> <span>Double</span><span>(</span><span>process<wbr>Info</span><span>.</span><span>physical<wbr>Memory</span><span>),</span>\n <span>unit</span><span>:</span> <span>.</span><span>bytes</span><span>)</span>\n <span>.</span><span>converted</span><span>(</span><span>to</span><span>:</span> <span>.</span><span>gigabytes</span><span>)</span> <span>// 16GB</span>\n </code></pre>\n<p>It\u2019s hard to get a sense of\n<a rel=\"noopener noreferrer\" href=\"https://everymac.com/systems/by_capability/minimum-macos-supported.html\">how many different Mac models are in use</a>,\nbut a reasonable estimate would be on the order of 2<sup>6</sup> or 2<sup>7</sup>.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#cellular-network-2-bits\"></a>Cellular Network (~2 bits)</h3>\n<p>Knowing whether someone\u2019s phone is on Verizon or Vodaphone\ncan also be factored into a fingerprint.\nYou can use the <code>CTTelephony<wbr>Network<wbr>Info</code> class from the\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/coretelephony\">CoreTelephony framework</a>\nto lookup the providers for devices with cellular service:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Core<wbr>Telephony</span>\n <span>let</span> <span>network<wbr>Info</span> <span>=</span> <span>CTTelephony<wbr>Network<wbr>Info</span><span>()</span>\n <span>let</span> <span>carriers</span> <span>=</span> <span>network<wbr>Info</span><span>.</span><span>service<wbr>Subscriber<wbr>Cellular<wbr>Providers</span><span>?</span><span>.</span><span>values</span>\n <span>carriers</span><span>?</span><span>.</span><span>map</span> <span>{</span> <span>(</span><span>$0</span><span>.</span><span>mobile<wbr>Network<wbr>Code</span><span>,</span> <span>$0</span><span>.</span><span>mobile<wbr>Country<wbr>Code</span><span>)</span> <span>}</span>\n </code></pre>\n<p>The number of providers varies per country,\nbut using the 4 major carriers in United States\nas a guideline,\nwe can say carrier information would contribute about 2 bits\n(or more if you have multiple SIM cards installed).</p>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#communication-preferences-2-bits\"></a>Communication Preferences (2 bits)</h2>\n<p>More generally,\neven knowing whether someone can send texts or email at all\ncan be factored into a fingerprint.\nThis information can be gathered without permissions via\nthe <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/messageui\">MessageUI framework</a>.</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>Message<wbr>UI</span>\n <span>MFMail<wbr>Compose<wbr>View<wbr>Controller</span><span>.</span><span>can<wbr>Send<wbr>Mail</span><span>()</span>\n <span>MFMessage<wbr>Compose<wbr>View<wbr>Controller</span><span>.</span><span>can<wbr>Send<wbr>Text</span><span>()</span>\n </code></pre>\n<h2>\n<a href=\"https://nshipster.com/device-identifiers/#additional-sources-of-identifying-information\"></a>Additional Sources of Identifying Information</h2>\n<p>If the use of digital fingerprinting seems outlandish,\nthat\u2019s just scratching the surface of how companies and researchers\nhave figured out how to circumvent your privacy.</p>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#geoip-and-relative-network-speeds\"></a>GeoIP and Relative Network Speeds</h3>\n<p>Although access to geolocation through conventional APIs\nrequires explicit authorization,\nthird parties may be able to get a general sense of where you are in the world\nbased on how you access the Internet.</p>\n<p><a rel=\"noopener noreferrer\" href=\"https://ipinfo.io\">Geolocation by source IP address</a>\nis used extensively for things like region locking and localization.\nYou could also combine this information with\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/library/archive/samplecode/SimplePing/Introduction/Intro.html#//apple_ref/doc/uid/DTS10000716\">ping-time measurements</a>\nto hosts in known locations\nto get a more accurate pinpoint on location <a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#weinberg_2018\">(Weinberg, Cho, Christin, Sekar, &amp; Gill, 2018)</a>:</p>\n<pre data-lang=\"Terminal\"><code><span>ping -c 5 99.24.18.13 #</span> San Francisco, USA\n <span>\n--- 99.24.18.13 ping statistics ---\n5 packets transmitted, 5 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 11.900/12.184/12.895/0.380 ms\n</span><span>ping -c 5 203.47.10.37 #</span> Adelaide, Australia\n <span>\n--- 203.47.10.37 ping statistics ---\n5 packets transmitted, 5 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 202.122/202.433/203.436/0.504 ms\n</span></code></pre>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#battery-health\"></a>Battery Health</h3>\n<p>It\u2019s unclear whether this is a concern in iOS,\nbut depending on how precise the results of <code>UIDevice</code> battery APIs are,\nyou may be able to use them to identify a device by its battery health.\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#olejnik_2015\">(Olejnik, Acar, Castelluccia, &amp; Diaz, 2016)</a></p>\n<pre data-lang=\"Swift\"><code><span>var</span> <span>timestamped<wbr>Battery<wbr>Levels</span><span>:</span> <span>[(</span><span>Date</span><span>,</span> <span>Float</span><span>)]</span> <span>=</span> <span>[]</span>\n <span>if</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>is<wbr>Battery<wbr>Monitoring<wbr>Enabled</span> <span>{</span>\n <span>timestamped<wbr>Battery<wbr>Levels</span><span>.</span><span>append</span><span>((</span><span>Date</span><span>(),</span> <span>UIDevice</span><span>.</span><span>current</span><span>.</span><span>battery<wbr>Level</span><span>))</span>\n <span>}</span>\n </code></pre>\n<aside>\n<p>For this reason,\nbattery level APIs were\n<a rel=\"noopener noreferrer\" href=\"https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API\">removed in Firefox 55</a>.</p>\n<p>If this seems outlandish,\nconsider that Apple recently released a security update for iOS after researchers\ndemonstrated that small discrepancies in gyroscope calibration settings\ncould be used to uniquely identify devices.\n<a rel=\"noopener noreferrer\" href=\"https://nshipster.com/device-identifiers/#zhang_2019\">(Zhang, Beresford, &amp; Sheret, 2019)</a></p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/device-identifiers/#and-so-on\"></a>And so on\u2026</h3>\n<p>Everything from your heartbeat, to your gait, to your\n<a rel=\"noopener noreferrer\" href=\"https://www.wired.co.uk/article/surveillance-technology-biometrics\">butt shape</a>\nseem capable of leaking your identity.\nIt can all be quite overwhelming.</p>\n<p>I mean,\nif a motivated individual can find your home address by\n<a rel=\"noopener noreferrer\" href=\"https://www.theverge.com/2019/10/11/20910551/stalker-attacked-pop-idol-reflection-pupils-selfies-videos-photos-google-street-view-japan\">cross-referencing the reflection in your eyes against Google Street view</a>,\nhow can we even stand a chance out there?</p>\n<hr>\n<p>Much as we may bemoan the current duopoly of mobile operating systems,\nwe might take some solace in the fact that\nat least one of the players actually cares about user privacy.\nThough it\u2019s unclear whether that\u2019s a fight that can ever be won.</p>\n<p>At times,\nour fate of being tracked and advertised to\nmay seem as inevitable as the victims in <em>It Follows</em>.</p>\n<p>But let\u2019s not forget that,\nas technologists, as people with a voice,\nwe\u2019re in a position to fight back.</p>\n<footer>\n<h5>References</h5>\n<ol>\n<li><span>de Montjoye, Y.-A., Hidalgo, C. A., Verleysen, M., &amp; Blondel, V. D. (2013). Unique in the Crowd: The privacy bounds of human mobility. <i>Scientific Reports</i>, <i>3</i>, 1376. https://doi.org/10.1038/srep01376</span></li>\n<li><span>Sweeney, L. (2000). Simple Demographics Often Identify People Uniquely. <i>Carnegie Mellon University, Data Privacy</i>. Working paper. Retrieved from http://dataprivacylab.org/projects/identifiability/</span></li>\n<li><span>Weinberg, Z., Cho, S., Christin, N., Sekar, V., &amp; Gill, P. (2018). How to Catch when Proxies Lie: Verifying the Physical Locations of Network Proxies with Active Geolocation. In <i>Proceedings of the Internet Measurement Conference 2018</i> (pp. 203\u2013217). New York, NY, USA: ACM. https://doi.org/10.1145/3278532.3278551</span></li>\n<li><span>Olejnik, \\L., Acar, G., Castelluccia, C., &amp; Diaz, C. (2016). The Leaking Battery. In <i>Revised Selected Papers of the 10th International Workshop on Data Privacy Management, and Security Assurance - Volume 9481</i> (pp. 254\u2013263). New York, NY, USA: Springer-Verlag New York, Inc. https://doi.org/10.1007/978-3-319-29883-2_18</span></li>\n<li><span>Zhang, J., Beresford, A. R., &amp; Sheret, I. (2019). SensorID: Sensor Calibration Fingerprinting for Smartphones. In <i>Proceedings of the 40th IEEE Symposium on Security and Privacy (SP)</i>. IEEE.</span></li>\n</ol>\n</footer>\n<img width=\"1\" alt=\"\" src=\"http://feeds.feedburner.com/~r/NSHipster/~4/hXb7k2avhFE\" height=\"1\">"
},
"visual": {
"url": "http://www.blogcdn.com/www.engadget.com/media/2013/10/irlbanner-1382819058.jpg",
"width": 620,
"height": 194,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 5062,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/e31b3fcb-27f6-4f3e-b96c-53902586e366",
"label": "Weblogs"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572578867134
},
{
"originId": "https://inessential.com/2019/10/31/amateurs",
"fingerprint": "a55a55f8",
"id": "+jHfsXnBCVfCstSIW1WDumAyigT4rnsUPnI5WFxgnAU=_16e23877a2b:1e9e9:d4506071",
"summary": {
"direction": "ltr",
"content": "<p>One thing I\u2019m weirdly proud of is my position as an amateur programmer.</p>\n<p>When I point that out, people say, \u201cWell, but\u2026\u201d \u2014\u00a0and I know where they\u2019re going, that after 25 years of professional experience I\u2019m not what you think of when you think of \u201camateur.\u201d</p>\n<p>And yet, it\u2019s still <em>true</em>. It\u2019s just that I\u2019ve come out the other side, and now I get to work on exactly what I want to, the way I want to, without any thoughts of trying to make money at it.</p>\n<p>I can take risks! I can work with anybody who shows up! It\u2019s a pure thrill. It\u2019s like writing single-malt apps.</p>\n<p>And I would wish for more people to find themselves in this position \u2014 eventually, anyway \u2014\u00a0because I want to see what <em>they</em> would make.</p>\n<p>PS The Dictionary app on my Mac says of the origin of the word \u201camateur\u201d:</p>\n<blockquote>\n<p>late 18th century: from French, from Italian <em>amatore</em>, from Latin <em>amator</em> \u2018lover\u2019, from <em>amare</em> \u2018to love\u2019.</p>\n</blockquote>\n<p>Spot-on.</p>"
},
"alternate": [
{
"href": "https://inessential.com/2019/10/31/amateurs",
"type": "text/html"
}
],
"crawled": 1572554111531,
"title": "Amateurs",
"published": 1572553200000,
"origin": {
"streamId": "feed/http://ranchero.com/xml/rss.xml",
"htmlUrl": "https://inessential.com/",
"title": "inessential.com"
},
"visual": {
"url": "none"
},
"unread": true,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572578844326
},
{
"originId": "tag:daringfireball.net,2019:/linked//6.36212",
"fingerprint": "9dc675e8",
"id": "ZTHt7g74IlVC5A2IgEvcn/aop5teo99gzFaGU2TCGxs=_16e23dce615:1ec09:d4506071",
"updated": 1572557257000,
"author": "John Gruber",
"alternate": [
{
"href": "https://twitter.com/jack/status/1189634360472829952",
"type": "text/html"
}
],
"crawled": 1572559709717,
"title": "Twitter to Stop Accepting Political Ads Globally",
"published": 1572557256000,
"origin": {
"streamId": "feed/http://daringfireball.net/index.xml",
"htmlUrl": "https://daringfireball.net/",
"title": "Daring Fireball"
},
"content": {
"direction": "ltr",
"content": "<p>Jack Dorsey, in a tweet thread:</p>\n<blockquote>\n<p>For instance, it\u2018s not credible for us to say: \u201cWe\u2019re working hard\nto stop people from gaming our systems to spread misleading info,\nbuuut if someone pays us to target and force people to see their\npolitical ad\u2026 well\u2026 they can say whatever they want!\u201d [\u2026]</p>\n<p>This isn\u2019t about free expression. This is about paying for reach.\nAnd paying to increase the reach of political speech has\nsignificant ramifications that today\u2019s democratic infrastructure\nmay not be prepared to handle. It\u2019s worth stepping back in order\nto address.</p>\n</blockquote>\n<p>Political advertising is a drop in the bucket of Twitter\u2019s overall revenue, but that\u2019s true of Facebook too. \u201c<em>The money matters to us</em>\u201d would be a terrible justification for Facebook\u2019s policy of allowing political ads to spread falsehoods, but the money <em>doesn\u2019t</em> even matter to them. Facebook is allowing political ads to spread falsehoods because Facebook wants political ads to spread falsehoods. There\u2019s no other explanation.</p>\n<div>\n<a title=\"Permanent link to \u2018Twitter to Stop Accepting Political Ads Globally\u2019\" href=\"https://daringfireball.net/linked/2019/10/31/twitter-political-ads\">\u00a0\u2605\u00a0</a>\n</div>"
},
"unread": true,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572578825713
},
{
"originId": "tag:blogger.com,1999:blog-8954608646904080796.post-6562845574025332924",
"fingerprint": "57441f5a",
"thumbnail": [
{
"url": "https://1.bp.blogspot.com/-nSjfvVEYsOE/XZ6cEycVw3I/AAAAAAAADTI/UMdV1Seh7R8c0GdV2RgwjuAoJLW47it1gCLcBGAsYHQ/s72-c/074.jpg",
"width": 72,
"height": 72
}
],
"id": "v0v+7Ya8tssIZvd3/pcnFRr3HwvY/5YK3FGc2t65c0Y=_16db6126dbf:685a:d4506071",
"updated": 1570675865985,
"author": "Edward Feser",
"alternate": [
{
"href": "http://edwardfeser.blogspot.com/2019/10/transubstantiation-and-hylemorphism.html",
"type": "text/html"
}
],
"crawled": 1570717724095,
"title": "Transubstantiation and hylemorphism",
"published": 1570675860000,
"origin": {
"streamId": "feed/http://edwardfeser.blogspot.com/feeds/posts/default",
"htmlUrl": "http://edwardfeser.blogspot.com/",
"title": "Edward Feser"
},
"content": {
"direction": "ltr",
"content": "<br><div><a href=\"https://1.bp.blogspot.com/-nSjfvVEYsOE/XZ6cEycVw3I/AAAAAAAADTI/UMdV1Seh7R8c0GdV2RgwjuAoJLW47it1gCLcBGAsYHQ/s1600/074.jpg\"><img border=\"0\" src=\"https://1.bp.blogspot.com/-nSjfvVEYsOE/XZ6cEycVw3I/AAAAAAAADTI/UMdV1Seh7R8c0GdV2RgwjuAoJLW47it1gCLcBGAsYHQ/s200/074.jpg\" data-original-width=\"171\" width=\"151\" data-original-height=\"226\" height=\"200\"></a></div><div><span>One of the key themes of the early modern philosophers\u2019 revolt against Scholasticism was a move away from an Aristotelian hylemorphist conception of the nature of physical substance to some variation or other of the mechanical philosophy.\u00a0 The other day I was asked a very interesting question: Can transubstantiation be formulated in terms of a mechanistic conception of physical substance rather than a hylemorphic one?\u00a0 My answer was that I would not peremptorily say that it cannot be, but that the suggestion certainly raises serious philosophical and theological problems.</span></div><a name=\"more\"></a><br> <div><span>Here\u2019s why.<span>\u00a0 </span>Hylemorphism in its most straightforward version roughly agrees with common sense about which of the things of everyday experience are distinct substances, which are different parts of the same substance, and which are aggregates rather than true substances.<span>\u00a0 </span>For example, it would say that a stone, a tree, and a dog are all distinct substances from one another; that a particular dog\u2019s nose and its right front leg are different parts of the same substance rather than distinct substances; and that a pile of stones is an aggregate rather than a substance in its own right.<span>\u00a0 </span>Of course, use of the term \u201csubstance\u201d in the technical Aristotelian sense isn\u2019t part of common sense, but even untutored common sense would surely involve the supposition that a stone, a tree, and a dog are all distinct <i>things</i> or <i>objects</i>, that the nose and leg of the dog are parts of a larger thing or object rather than separate things or objects, and that a pile of stones is a bunch of things or objects rather than a single object.<span>\u00a0 </span>At least to that extent, common sense would more or less agree with what I am calling a straightforward version of hylemorphism. <span>\u00a0</span>(See chapter 3 of <i><a href=\"https://www.amazon.com/Scholastic-Metaphysics-A-Contemporary-Introduction/dp/3868385444/ref=sr_1_1?ie=UTF8&qid=1391482601&sr=8-1&keywords=scholastic+metaphysics+a+contemporary+introduction\">Scholastic Metaphysics</a></i> for exposition and defense of the hylemorphist account of substance.)</span></div><div><span><br></span></div><div><span>Now, the mechanical world picture that pushed aside the hylemorphist model tended radically to revise the common sense understanding of physical objects in one of two general ways, depending on how mechanism was spelled out.<span>\u00a0 </span>It reduced ordinary physical objects either to mere aggregates of their innumerably many component parts, or to mere modes of some larger blob of which <i>they</i>were the parts.</span></div><div><span><br></span></div><div><span>Descartes and Spinoza essentially took the latter option.<span>\u00a0 </span>Though Descartes is often described as positing a plurality of extended substances alongside the plurality of thinking substances, his considered view seemed to be that strictly speaking, there is only a single extended substance, of which the ordinary objects of our experience are merely modifications.<span>\u00a0 </span>Spinoza more famously took such a position (or rather, he took it that <i>Deus sive Natura</i>was the one substance of which the ordinary physical objects of our experience are all modes).<span>\u00a0 </span>On this view, a stone, a tree, and a dog are not really distinct substances, but merely distinct aspects of one and the same substance \u2013 in something like the way common sense regards the color, weight, and shape of a stone to be mere modes of one and the same object, the stone.</span></div><div><span><br></span></div><div><span>Atomist and corpuscularian versions of the mechanical philosophy went in the other direction.<span>\u00a0 </span>They essentially make either atoms or corpuscles the true substances, and ordinary objects mere aggregates of these purported substances.<span>\u00a0 </span>Just as a pile of rocks is not a true substance but merely a collection of substances (or as the hylemorphist would say, being a pile of rocks is a merely accidental form rather than a substantial form), so too a stone, a tree, or a dog is on this view merely a collection of particles.<span>\u00a0 </span>In effect, the particles are the true substances, and the stone, tree, or dog is like the pile \u2013 a relatively superficial arrangement of metaphysically more fundamental entities.</span></div><div><span><br></span></div><div><span>So, to come to transubstantiation, the idea, of course, is that in the Eucharist, while the accidents of bread and wine remain, the substance of bread and wine are miraculously replaced with that of Christ.<span>\u00a0 </span>Suppose, then, that we were to adopt Descartes\u2019 version of the mechanical philosophy, on which there is just one big physical substance underlying all the things ordinary perceptual experience reveals to us.<span>\u00a0 </span>That would entail that the substance that underlies the accidents of bread and wine that are about to be consecrated is the very same substance as that which underlies stones, trees, dogs, cats, human bodies, apples, oranges, the sun, the moon, water, lead, gold, and every other thing we see, hear, taste, touch, or smell.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>But in that case, when transubstantiation occurs, it is not just the substance underlying the accidents of bread and wine that is replaced, but the substance underlying all of these other things too.<span>\u00a0 </span>In other words, after transubstantiation occurs, it is really the body and blood of Christ that underlies what we perceive as stones, trees, dogs, cats, human bodies, the sun, the moon, water, etc.!<span>\u00a0 </span><i>Everything</i> in the physical world would be transubstantiated.<span>\u00a0 </span>We would be left with a kind of pantheism.<span>\u00a0 </span>Absolutely <i>every</i> physical thing would have to treated with the same reverence that the Eucharist is, because every physical thing would be the Eucharist!</span></div><div><span><br></span></div><div><span>Another bizarre implication of this is that transubstantiation could occur only once.<span>\u00a0 </span>For only at the first time it occurs is the one physical substance replaced by that of Christ.<span>\u00a0 </span>If a priest were ever to try to consecrate bread and wine again, he would fail, because there is no longer any physical substance there to <i>be</i> replaced.<span>\u00a0 </span>It is <i>already</i>the body and blood of Christ.</span></div><div><span><br></span></div><div><span>Suppose we went the other route, that of either atomism or corpuscularianism.<span>\u00a0 </span>Then, like stones, trees, and dogs, bread and wine would not be true substances but merely accidental collections of innumerably many true substances.<span>\u00a0 </span>They would be like a pile of rocks, only instead it would be fundamental particles (atoms or corpuscles, depending on your favored version of the mechanical philosophy) that would be piled up.<span>\u00a0 </span>But in that case, exactly <i>what </i>is the substance that is replaced when transubstantiation occurs?<span>\u00a0 </span>Neither the substance of the bread nor that of the wine can be what is replaced, because on this view they just <i>aren\u2019t</i>true substances in the first place.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Should we say that it is each particle that makes up the aggregate that is transubstantiated (just as Catholic theology allows that many hosts at a time may be consecrated at Mass)?<span>\u00a0\u00a0 </span>But there are several problems with that suggestion.<span>\u00a0 </span>The first is that it is hard to know how to give a principled answer to the question what the <i>boundaries</i> are between those particles that make up the aggregate and those that are not part of it \u2013 and thus between those particles that are transubstantiated, and those that are not.<span>\u00a0 </span>The reason is that the boundaries of an aggregate are much less well defined than those of a substance.<span>\u00a0 </span>Is a stone that is two millimeters away from a pile of stones itself part of the pile or not?<span>\u00a0 </span>And is a particle that falls from the host part of <i>that</i> (purported) aggregate of particles or not?<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>If we think of the host on the model of an Aristotelian <i>substance</i>, then we can say that a fallen particle is part of the host, like a body part that has been severed, as it were.<span>\u00a0 </span>But, again, if instead we think in terms of the model of a pile of stones or some other aggregate, the answer isn\u2019t as clear.</span></div><div><span><br></span></div><div><span>A second problem is that in Catholic theology, not any old matter can be used when consecrating the Eucharist.<span>\u00a0 </span>It has to be bread and wine, specifically.<span>\u00a0 </span>But on the interpretation under consideration, according to which bread and wine are not true substances, it is really the particles (either atoms or corpuscles) that are being consecrated.<span>\u00a0 </span>And the atoms or corpuscles that make up bread and wine are essentially the same as those that make up everything else (just as the stones that make up a pile can be essentially of the same type as those that are used instead to make up a wall).<span>\u00a0 </span>In that case, though, it would be hard to see why there is anything special about bread and wine.<span>\u00a0 </span>Why couldn\u2019t <i>any</i> old physical thing be consecrated, if every physical thing is essentially just the same kind of stuff in relatively superficial differences of configuration?</span></div><div><span><br></span></div><div><span>A third problem is that canon law says that a Catholic ought to receive communion at most only once (or in some special circumstances, perhaps twice) a day.<span>\u00a0 </span>But on the interpretation under consideration, one would in effect be consuming <i>millions</i> of consecrated hosts insofar as each of the millions of particles that make up what common sense regards as a single host was being independently transubstantiated.</span></div><div><span><br></span></div><div><span>Perhaps such problems could be solved, though I am doubtful.\u00a0 Anyway, the issue illustrates the unexpected implications that philosophical assumptions can have for theology.\u00a0 (And thus the caution that any Catholic ought to exercise before embracing philosophical novelties.\u00a0 The Scholastics knew what they were doing.)</span></div>"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/qEtBNTmNKQvUro7McqHPXL-2OOM=/0x0:6720x4480/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59664621/marshall_majorIII_aniconinthemaking_product_rgb_highres_2.0.jpg",
"width": 1310,
"height": 878,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 3562,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/fbdcd69b-7e27-4b6a-bfed-6584b944155d",
"label": "\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572500351314
},
{
"keywords": [
"Xcode"
],
"originId": "https://nshipster.com/metrickit",
"recrawled": 1572363511019,
"updateCount": 2,
"fingerprint": "9be1d5cd",
"id": "CmHb1hXBWguYpGAhzgwJM9xvPVSYJFbt7KLqF3nqYQ0=_16df9da832d:14579:d4506071",
"updated": 1571641200000,
"author": "Mattt",
"summary": {
"direction": "ltr",
"content": "<p>At WWDC this year, Apple announced a coordinated effort between Xcode 11 and iOS 13 to bring new insights to developers about how their apps are performing in the field.</p>"
},
"alternate": [
{
"href": "http://feedproxy.google.com/~r/NSHipster/~3/o2-j6xKjBrA/",
"type": "text/html"
}
],
"canonical": [
{
"href": "https://nshipster.com/metrickit/",
"type": "text/html"
}
],
"crawled": 1571854910253,
"title": "MetricKit",
"published": 1571641200000,
"origin": {
"streamId": "feed/http://feeds.feedburner.com/NSHipster",
"htmlUrl": "https://nshipster.com/",
"title": "NSHipster"
},
"content": {
"direction": "ltr",
"content": "<p>As an undergraduate student,\nI had a radio show called\n<em>\u201cGoodbye, Blue Monday\u201d</em>\n(I was really into Vonnegut at the time).\nIt was nothing glamorous \u2014\njust a weekly, 2-hour slot at the end of the night\nbefore the station switched into automation.</p>\n<p>If you happened to be driving through the hills of Pittsburgh, Pennsylvania\nlate at night with your radio tuned to\n<a rel=\"noopener noreferrer\" href=\"http://www.wrct.org\"><abbr title=\"Radio Carnegie Tech\">WRCT</abbr> 88.3</a>,\nyou\u2019d have heard an eclectic mix of\n<a rel=\"noopener noreferrer\" href=\"https://beta.music.apple.com/us/album/acoustica/410402556\">Contemporary Classical</a>,\n<a rel=\"noopener noreferrer\" href=\"https://beta.music.apple.com/us/album/a-funk-odyssey/203132910\">Acid Jazz</a>,\n<a rel=\"noopener noreferrer\" href=\"https://beta.music.apple.com/us/album/ma-quale-idea-single/1415038751\">Italian Disco</a>, and\n<a rel=\"noopener noreferrer\" href=\"https://beta.music.apple.com/us/album/kind-of-blue/268443092\">Bebop</a>.\nThat, and the stilting, dulcet baritone of\na college kid doing his best impersonation of\n<a rel=\"noopener noreferrer\" href=\"http://old.post-gazette.com/magazine/20010404mowod4.asp\">Tony Mowod</a>.</p>\n<p>Sitting there in the booth,\nwaiting for tracks to play out before launching into an\n<abbr title=\"Federal Communications Commission\">FCC</abbr>-mandated\n<abbr title=\"Public Service Announcement\">PSA</abbr>\nor on-the-hour\n<a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Station_identification\">station identification</a>,\nI\u2019d wonder:\n<em>Is anyone out there listening?</em>\n<em>And if they were, did they like it?</em>\nI could\u2019ve been broadcasting static the whole time and been none the wiser.</p>\n<p>The same thoughts come to mind whenever I submit a build to App Store Connect\u2026\nbut then I\u2019ll remember that, unlike radio,\nyou <em>can</em> actually know these things!\nAnd the latest improvements in Xcode 11 make it easier than ever\nto get an idea of how your apps are performing in the field.</p>\n<p>We\u2019ll cover everything you need to know in this week\u2019s NSHipster article.\nSo as they say on the radio:\n<em>\u201cDon\u2019t touch that dial (it\u2019s got jam on it)\u201d.</em></p>\n<hr>\n<p>MetricKit is a new framework in iOS 13\nfor collecting and processing battery and performance metrics.\nIt was announced at <a href=\"https://nshipster.com/wwdc-2019/\">WWDC this year</a>\nalong with XCTest Metrics and the Xcode Metrics Organizer\nas part of a coordinated effort to bring new insights to developers\nabout how their apps are performing in the field.</p>\n<figure>\n<picture>\n<source srcset=\"https://nshipster.com/assets/metrickit-diagram--dark-b7358fa1e9bdf87502044ad241ffcfe5bb904cb7d1bf948d38b0f35b3e93fc59.png\" media=\"(prefers-color-scheme: dark)\">\n<img alt=\"MetricKit Diagram\" src=\"https://nshipster.com/assets/metrickit-diagram--light-0dd5dd5af36c169873c46b2be3155f50aa5ccbfd98a8794bdd5e0d510eb9a42d.png\">\n</picture>\n<figcaption>Diagram from WWDC 2019 Session 417: <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/videos/play/wwdc2019/417/\">&quot;Improving Battery Life and Performance&quot;</a></figcaption>\n</figure>\n<p>Apple automatically collects metrics from apps installed on the App Store.\nYou can view them in Xcode 11\nby opening the Organizer (<kbd>\u2325</kbd><kbd>\u2318</kbd><kbd>\u21e7</kbd><kbd>O</kbd>)\nand selecting the new Metrics tab.</p>\n<p>MetricKit complement Xcode Organizer Metrics by providing a programmatic way to\nreceive daily information about how your app is performing in the field.\nWith this information,\nyou can collect, aggregate, and analyze on your own in greater detail\nthan you can through Xcode.</p>\n<h2>\n<a href=\"https://nshipster.com/metrickit/#understanding-app-metrics\"></a>Understanding App Metrics</h2>\n<p>Metrics can help uncover issues you might not have seen while testing locally,\nand allow you to track changes across different versions of your app.\nFor this initial release,\nApple has focused on the two metrics that matter most to users:\n<dfn>battery usage</dfn> and <dfn>performance</dfn>.</p>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#battery-usage\"></a>Battery Usage</h3>\n<picture>\n<source srcset=\"https://nshipster.com/assets/metrickit-battery-usage--dark-a04760e5bcf6bb9da1153f10b37c074d3ecee9cba5887cf1065bee0a4e545d95.png\" media=\"(prefers-color-scheme: dark)\">\n<img alt=\"MetricKit Diagram\" src=\"https://nshipster.com/assets/metrickit-battery-usage--light-999245ecb5a77e77835214efce028de4fe854da718ef7b7b86730ad8812383ca.png\">\n</picture>\n<p>Battery life depends on a lot of different factors.\nPhysical aspects like the age of the device and\nthe number of charge cycles are determinative,\nbut the way your phone is used matters, too.\nThings like CPU usage,\nthe brightness of the display and the colors on the screen,\nand how often radios are used to fetch data or get your current location \u2014\nall of these can have a big impact.\nBut the main thing to keep in mind is that\nusers care a lot about battery life.</p>\n<p>Aside from how good the camera is,\nthe amount of time between charges\nis <em>the</em> deciding factor when someone buys a new phone these days.\nSo when their new, expensive phone <em>doesn\u2019t</em> make it through the day,\nthey\u2019re going to be pretty unhappy.</p>\n<p>Until recently,\nApple\u2019s taken most of the heat on battery issues.\nBut since iOS 12 and its new\n<a rel=\"noopener noreferrer\" href=\"https://support.apple.com/en-us/HT201264\">Battery Usage screen</a> in Settings,\nusers now have a way to tell when their favorite app is to blame.\nFortunately,\nwith iOS 13 you now have everything you need to make sure\nyour app doesn\u2019t run afoul of reasonable energy usage.</p>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#performance\"></a>Performance</h3>\n<p>Performance is another key factor in the overall user experience.\nNormally, we might look to stats like\nprocessor clock speed or <a href=\"https://nshipster.com/uitableviewheaderfooterview/\">frame rate</a>\nas a measure of performance.\nBut instead,\nApple\u2019s focusing on less abstract and more actionable metrics:</p>\n<dl>\n<dt>Hang Rate</dt>\n<dd>How often is the main / UI thread blocked,\nsuch that the app is unresponsive to user input?</dd>\n<dt>Launch Time</dt>\n<dd>How long does an app take to become usable after the user taps its icon?</dd>\n<dt>Peak Memory &amp; Memory at Suspension</dt>\n<dd>How much memory does the app use at its peak\nand just before entering the background?</dd>\n<dt>Disk Writes</dt>\n<dd>How often does the app write to disk,\nwhich \u2014 if you didn\u2019t already know \u2014 is a\n<a rel=\"noopener noreferrer\" href=\"https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html\">comparatively slow operation</a>\n<em>(even with the flash storage on an iPhone!)</em>\n</dd>\n</dl>\n<h2>\n<a href=\"https://nshipster.com/metrickit/#using-metrickit\"></a>Using MetricKit</h2>\n<p>From the perspective of an API consumer,\nit\u2019s hard to imagine how MetricKit could be easier to incorporate.\nAll you need is for some part of your app to serve as\na metric subscriber\n(an obvious choice is your <code>App<wbr>Delegate</code>),\nand for it to be added to the shared <code>MXMetric<wbr>Manager</code>:</p>\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>import</span> <span>Metric<wbr>Kit</span>\n <span>@UIApplication<wbr>Main</span>\n <span>class</span> <span>App<wbr>Delegate</span><span>:</span> <span>UIResponder</span><span>,</span> <span>UIApplication<wbr>Delegate</span> <span>{</span>\n <span>func</span> <span>application</span><span>(</span><span>_</span> <span>application</span><span>:</span> <span>UIApplication</span><span>,</span> <span>did<wbr>Finish<wbr>Launching<wbr>With<wbr>Options</span> <span>launch<wbr>Options</span><span>:</span> <span>[</span><span>UIApplication</span><span>.</span><span>Launch<wbr>Options<wbr>Key</span><span>:</span> <span>Any</span><span>]?)</span> <span>-></span> <span>Bool</span> <span>{</span>\n <span>MXMetric<wbr>Manager</span><span>.</span><span>shared</span><span>.</span><span>add</span><span>(</span><span>self</span><span>)</span>\n <span>return</span> <span>true</span>\n <span>}</span>\n <span>func</span> <span>application<wbr>Will<wbr>Terminate</span><span>(</span><span>_</span> <span>application</span><span>:</span> <span>UIApplication</span><span>)</span> <span>{</span>\n <span>MXMetric<wbr>Manager</span><span>.</span><span>shared</span><span>.</span><span>remove</span><span>(</span><span>self</span><span>)</span>\n <span>}</span>\n <span>}</span>\n <span>extension</span> <span>App<wbr>Delegate</span><span>:</span> <span>MXMetric<wbr>Manager<wbr>Subscriber</span> <span>{</span>\n <span>func</span> <span>did<wbr>Receive</span><span>(</span><span>_</span> <span>payloads</span><span>:</span> <span>[</span><span>MXMetric<wbr>Payload</span><span>])</span> <span>{</span>\n <var>...</var>\n <span>}</span>\n <span>}</span>\n </code></pre>\n<p>iOS automatically collects samples while your app is being used,\nand once per day (every 24 hours),\nit\u2019ll send an aggregated report with those metrics.</p>\n<p>To verify that your <code>MXMetric<wbr>Manager<wbr>Subscriber</code>\nis having its delegate method called as expected,\nselect Simulate MetricKit Payloads from the Debug menu\nwhile Xcode is running your app.</p>\n<aside>\n<p>The Simulate MetricKit Payloads menu item\nrequires the app to be running on an actual device\nand is disabled for Simulator builds.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#annotating-critical-code-sections-with-signposts\"></a>Annotating Critical Code Sections with Signposts</h3>\n<p>In addition to the baseline statistics collected for you,\nyou can use the\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/metrickit/3214364-mxsignpost\"><code>mx<wbr>Signpost</code></a> function\nto collect metrics around the most important parts of your code.\nThis <a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/os/3019241-os_signpost\">signpost</a>-backed API\ncaptures CPU time, memory, and writes to disk.</p>\n<p>For example,\nif part of your app did post-processing on audio streams,\nyou might annotate those regions with metric signposts\nto determine the energy and performance impact of that work:</p>\n<pre data-lang=\"Swift\"><code><span>let</span> <span>audio<wbr>Log<wbr>Handle</span> <span>=</span> <span>MXMetric<wbr>Manager</span><span>.</span><span>make<wbr>Log<wbr>Handle</span><span>(</span><span>category</span><span>:</span> <span>&quot;Audio&quot;</span><span>)</span>\n <span>func</span> <span>process<wbr>Audio<wbr>Stream</span><span>()</span> <span>{</span>\n <span>mx<wbr>Signpost</span><span>(</span><span>.</span><span>begin</span><span>,</span> <span>log</span><span>:</span> <span>audio<wbr>Log<wbr>Handle</span><span>,</span> <span>name</span><span>:</span> <span>&quot;Process<wbr>Audio<wbr>Stream&quot;</span><span>)</span>\n <var>...</var>\n <span>mx<wbr>Signpost</span><span>(</span><span>.</span><span>end</span><span>,</span> <span>log</span><span>:</span> <span>audio<wbr>Log<wbr>Handle</span><span>,</span> <span>name</span><span>:</span> <span>&quot;Process<wbr>Audio<wbr>Stream&quot;</span><span>)</span>\n <span>}</span>\n </code></pre>\n<h2>\n<a href=\"https://nshipster.com/metrickit/#creating-a-self-hosted-web-service-for-collecting-app-metrics\"></a>Creating a Self-Hosted Web Service for Collecting App Metrics</h2>\n<p>Now that you have this information,\nwhat do you do with it?\nHow do we fill that <code><var>...</var></code> placeholder in our implementation of <code>did<wbr>Receive(_:)</code>?</p>\n<p>You <em>could</em> pass that along to some paid analytics or crash reporting service,\n<em>but where\u2019s the fun in that</em>?\nLet\u2019s build our own web service to collect these for further analysis:</p>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#storing-and-querying-metrics-with-postgresql\"></a>Storing and Querying Metrics with PostgreSQL</h3>\n<p>The <code>MXMetric<wbr>Payload</code> objects received by metrics manager subscribers\nhave a convenient\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/metrickit/mxmetricpayload/3131907-jsonrepresentation\"><code>json<wbr>Representation()</code></a> method\nthat generates something like this:</p>\n<details>\n\n<pre data-lang=\"JSON\"><code><span>{</span><span>\n</span><span>&quot;location<wbr>Activity<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>Best<wbr>Accuracy<wbr>For<wbr>Navigation<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;20 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Best<wbr>Accuracy<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;30 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Hundred<wbr>Meters<wbr>Accuracy<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;30 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Nearest<wbr>Ten<wbr>Meters<wbr>Accuracy<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;30 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Kilometer<wbr>Accuracy<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;20 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Three<wbr>Kilometers<wbr>Accuracy<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;20 sec&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;cellular<wbr>Condition<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cell<wbr>Condition<wbr>Time&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>20</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;1 bars&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;1 bars&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>30</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;2 bars&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;2 bars&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>50</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;3 bars&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;3 bars&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;meta<wbr>Data&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;app<wbr>Build<wbr>Version&quot;</span><span>:</span><span> </span><span>&quot;0&quot;</span><span>,</span><span>\n</span><span>&quot;os<wbr>Version&quot;</span><span>:</span><span> </span><span>&quot;i<wbr>Phone OS 13.1.3 (17A878)&quot;</span><span>,</span><span>\n</span><span>&quot;region<wbr>Format&quot;</span><span>:</span><span> </span><span>&quot;US&quot;</span><span>,</span><span>\n</span><span>&quot;device<wbr>Type&quot;</span><span>:</span><span> </span><span>&quot;i<wbr>Phone9,2&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;gpu<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>GPUTime&quot;</span><span>:</span><span> </span><span>&quot;20 sec&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;memory<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;peak<wbr>Memory<wbr>Usage&quot;</span><span>:</span><span> </span><span>&quot;200,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;average<wbr>Suspended<wbr>Memory&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;average<wbr>Value&quot;</span><span>:</span><span> </span><span>&quot;100,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;standard<wbr>Deviation&quot;</span><span>:</span><span> </span><span>0</span><span>,</span><span>\n</span><span>&quot;sample<wbr>Count&quot;</span><span>:</span><span> </span><span>500</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;signpost<wbr>Metrics&quot;</span><span>:</span><span> </span><span>[</span><span>\n</span><span>{</span><span>\n</span><span>&quot;signpost<wbr>Interval<wbr>Data&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogrammed<wbr>Signpost<wbr>Durations&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>50</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;0 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;100 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>60</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;100 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;400 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>30</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;400 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;700 ms&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;signpost<wbr>Cumulative<wbr>CPUTime&quot;</span><span>:</span><span> </span><span>&quot;30,000 ms&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Average<wbr>Memory&quot;</span><span>:</span><span> </span><span>&quot;100,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Cumulative<wbr>Logical<wbr>Writes&quot;</span><span>:</span><span> </span><span>&quot;600 k<wbr>B&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;signpost<wbr>Category&quot;</span><span>:</span><span> </span><span>&quot;Test<wbr>Signpost<wbr>Category1&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Name&quot;</span><span>:</span><span> </span><span>&quot;Test<wbr>Signpost<wbr>Name1&quot;</span><span>,</span><span>\n</span><span>&quot;total<wbr>Signpost<wbr>Count&quot;</span><span>:</span><span> </span><span>30</span><span>\n</span><span>},</span><span>\n</span><span>{</span><span>\n</span><span>&quot;signpost<wbr>Interval<wbr>Data&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogrammed<wbr>Signpost<wbr>Durations&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>60</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;0 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;200 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>70</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;201 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;300 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>80</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;301 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;500 ms&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;signpost<wbr>Cumulative<wbr>CPUTime&quot;</span><span>:</span><span> </span><span>&quot;50,000 ms&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Average<wbr>Memory&quot;</span><span>:</span><span> </span><span>&quot;60,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Cumulative<wbr>Logical<wbr>Writes&quot;</span><span>:</span><span> </span><span>&quot;700 k<wbr>B&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;signpost<wbr>Category&quot;</span><span>:</span><span> </span><span>&quot;Test<wbr>Signpost<wbr>Category2&quot;</span><span>,</span><span>\n</span><span>&quot;signpost<wbr>Name&quot;</span><span>:</span><span> </span><span>&quot;Test<wbr>Signpost<wbr>Name2&quot;</span><span>,</span><span>\n</span><span>&quot;total<wbr>Signpost<wbr>Count&quot;</span><span>:</span><span> </span><span>40</span><span>\n</span><span>}</span><span>\n</span><span>],</span><span>\n</span><span>&quot;display<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;average<wbr>Pixel<wbr>Luminance&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;average<wbr>Value&quot;</span><span>:</span><span> </span><span>&quot;50 apl&quot;</span><span>,</span><span>\n</span><span>&quot;standard<wbr>Deviation&quot;</span><span>:</span><span> </span><span>0</span><span>,</span><span>\n</span><span>&quot;sample<wbr>Count&quot;</span><span>:</span><span> </span><span>500</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;cpu<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>CPUTime&quot;</span><span>:</span><span> </span><span>&quot;100 sec&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;network<wbr>Transfer<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>Cellular<wbr>Download&quot;</span><span>:</span><span> </span><span>&quot;80,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Wifi<wbr>Download&quot;</span><span>:</span><span> </span><span>&quot;60,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Cellular<wbr>Upload&quot;</span><span>:</span><span> </span><span>&quot;70,000 k<wbr>B&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Wifi<wbr>Upload&quot;</span><span>:</span><span> </span><span>&quot;50,000 k<wbr>B&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;disk<wbr>IOMetrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>Logical<wbr>Writes&quot;</span><span>:</span><span> </span><span>&quot;1,300 k<wbr>B&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;application<wbr>Launch<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogrammed<wbr>Time<wbr>To<wbr>First<wbr>Draw<wbr>Key&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>50</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;1,000 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;1,010 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>60</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;2,000 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;2,010 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>30</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;3,000 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;3,010 ms&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;histogrammed<wbr>Resume<wbr>Time&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>60</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;200 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;210 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>70</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;300 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;310 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>80</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;500 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;510 ms&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;application<wbr>Time<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;cumulative<wbr>Foreground<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;700 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Background<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;40 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Background<wbr>Audio<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;30 sec&quot;</span><span>,</span><span>\n</span><span>&quot;cumulative<wbr>Background<wbr>Location<wbr>Time&quot;</span><span>:</span><span> </span><span>&quot;30 sec&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;time<wbr>Stamp<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;2019-10-22 06:59:00 +0000&quot;</span><span>,</span><span>\n</span><span>&quot;application<wbr>Responsiveness<wbr>Metrics&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogrammed<wbr>App<wbr>Hang<wbr>Time&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;histogram<wbr>Num<wbr>Buckets&quot;</span><span>:</span><span> </span><span>3</span><span>,</span><span>\n</span><span>&quot;histogram<wbr>Value&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;0&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>50</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;0 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;100 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;1&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>60</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;100 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;400 ms&quot;</span><span>\n</span><span>},</span><span>\n</span><span>&quot;2&quot;</span><span>:</span><span> </span><span>{</span><span>\n</span><span>&quot;bucket<wbr>Count&quot;</span><span>:</span><span> </span><span>30</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>Start&quot;</span><span>:</span><span> </span><span>&quot;400 ms&quot;</span><span>,</span><span>\n</span><span>&quot;bucket<wbr>End&quot;</span><span>:</span><span> </span><span>&quot;700 ms&quot;</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>}</span><span>\n</span><span>},</span><span>\n</span><span>&quot;app<wbr>Version&quot;</span><span>:</span><span> </span><span>&quot;1.0.0&quot;</span><span>,</span><span>\n</span><span>&quot;time<wbr>Stamp<wbr>Begin&quot;</span><span>:</span><span> </span><span>&quot;2019-10-21 07:00:00 +0000&quot;</span><span>\n</span><span>}</span><span>\n</span></code></pre>\n</details>\n<p>As you can see,\nthere\u2019s a lot baked into this representation.\nDefining a schema for all of this information would be a lot of work,\nand there\u2019s no guarantee that this won\u2019t change in the future.\nSo instead,\nlet\u2019s embrace the NoSQL paradigm\n<em>(albeit responsibly, using <a rel=\"noopener noreferrer\" href=\"https://postgresapp.com\">Postgres</a>)</em>\nby storing payloads in a <a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/datatype-json.html\"><code>JSONB</code> column</a>:</p>\n<pre data-lang=\"sql\"><code><span>CREATE</span> <span>TABLE</span> <span>IF</span> <span>NOT</span> <span>EXISTS</span> <span>metrics</span> <span>(</span>\n <span>id</span> <span>BIGINT</span> <span>GENERATED</span> <span>BY</span> <span>DEFAULT</span> <span>AS</span> <span>IDENTITY</span> <span>PRIMARY</span> <span>KEY</span><span>,</span>\n <span>payload</span> <span>JSONB</span> <span>NOT</span> <span>NULL</span>\n <span>);</span>\n </code></pre>\n<p><em>So easy!</em></p>\n<p>We can extract individual fields from payloads\nusing <a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/functions-json.html\">JSON operators</a>\nlike so:</p>\n<pre data-lang=\"sql\"><code><span>SELECT</span> <span>(</span><span>payload</span> <span>-></span> <span>'application<wbr>Time<wbr>Metrics'</span>\n <span>->></span> <span>'cumulative<wbr>Foreground<wbr>Time'</span><span>)::</span><span>INTERVAL</span>\n <span>FROM</span> <span>metrics</span><span>;</span>\n <span>-- interval</span>\n <span>-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550</span>\n <span>-- @ 11 mins 40 secs</span>\n <span>-- (1 row)</span>\n </code></pre>\n<aside>\n<p>The JSON representation of metrics stores measurements for time and memory\nas strings with units (such as <code>&quot;100 ms&quot;</code> and <code>500 k<wbr>B</code>).\nIn Postgres, you can cast time measurements directly to the\n<a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT\"><code>INTERVAL</code> type</a>,\nhowever you\u2019ll need to create a function to convert to byte counts:</p>\n<pre data-lang=\"sql\"><code><span>CREATE</span> <span>OR</span> <span>REPLACE</span> <span>FUNCTION</span> <span>parse_byte_count</span> <span>(</span><span>TEXT</span><span>)</span>\n <span>RETURNS</span> <span>BIGINT</span>\n <span>AS</span> <span>$$</span>\n <span>SELECT</span>\n <span>replace</span><span>(</span><span>split_part</span><span>(</span><span>$</span><span>1</span><span>,</span> <span>' '</span><span>,</span> <span>1</span><span>),</span><span>','</span><span>,</span><span>''</span><span>)::</span><span>BIGINT</span> <span>*</span>\n <span>CASE</span> <span>split_part</span><span>(</span><span>$</span><span>1</span><span>,</span> <span>' '</span><span>,</span> <span>2</span><span>)</span>\n <span>WHEN</span> <span>'k<wbr>B'</span> <span>THEN</span> <span>1000</span>\n <span>WHEN</span> <span>'MB'</span> <span>THEN</span> <span>1000</span> <span>*</span> <span>1000</span>\n <span>WHEN</span> <span>'GB'</span> <span>THEN</span> <span>1000</span> <span>*</span> <span>1000</span> <span>*</span> <span>1000</span>\n <span>END</span>\n <span>$$</span> <span>LANGUAGE</span> <span>'sql'</span> <span>STRICT</span> <span>IMMUTABLE</span><span>;</span>\n </code></pre>\n</aside>\n<h4>\n<a href=\"https://nshipster.com/metrickit/#advanced-creating-views\"></a>Advanced: Creating Views</h4>\n<p>JSON operators in PostgreSQL can be cumbersome to work with \u2014\nespecially for more complex queries.\nOne way to help with that is to create a view\n<em>(<a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/rules-materializedviews.html\">materialized</a> or otherwise)</em>\nto project the most important information to you\nin the most convenient representation:</p>\n<pre data-lang=\"sql\"><code><span>CREATE</span> <span>VIEW</span> <span>key_performance_indicators</span> <span>AS</span>\n <span>SELECT</span>\n <span>id</span><span>,</span>\n <span>(</span><span>payload</span> <span>-></span> <span>'app<wbr>Version'</span><span>)</span> <span>AS</span> <span>app_version</span><span>,</span>\n <span>(</span><span>payload</span> <span>-></span> <span>'meta<wbr>Data'</span> <span>->></span> <span>'device<wbr>Type'</span><span>)</span> <span>AS</span> <span>device_type</span><span>,</span>\n <span>(</span><span>payload</span> <span>-></span> <span>'meta<wbr>Data'</span> <span>->></span> <span>'region<wbr>Format'</span><span>)</span> <span>AS</span> <span>region</span><span>,</span>\n <span>(</span><span>payload</span> <span>-></span> <span>'application<wbr>Time<wbr>Metrics'</span>\n <span>->></span> <span>'cumulative<wbr>Foreground<wbr>Time'</span>\n <span>)::</span><span>INTERVAL</span> <span>AS</span> <span>cumulative_foreground_time</span><span>,</span>\n <span>parse_byte_count</span><span>(</span>\n <span>payload</span> <span>-></span> <span>'memory<wbr>Metrics'</span>\n <span>->></span> <span>'peak<wbr>Memory<wbr>Usage'</span>\n <span>)</span> <span>AS</span> <span>peak_memory_usage_bytes</span>\n <span>FROM</span> <span>metrics</span><span>;</span>\n </code></pre>\n<p>With views,\nyou can perform\n<a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/functions-aggregate.html\">aggregate queries</a>\nover all of your metrics JSON payloads\nwith the convenience of a schema-backed relational database:</p>\n<pre data-lang=\"sql\"><code><span>SELECT</span> <span>avg</span><span>(</span><span>cumulative_foreground_time</span><span>)</span>\n <span>FROM</span> <span>key_performance_indicators</span><span>;</span>\n <span>-- avg</span>\n <span>-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550</span>\n <span>-- @ 9 mins 41 secs</span>\n <span>SELECT</span> <span>app_version</span><span>,</span> <span>percentile_disc</span><span>(</span><span>0</span><span>.</span><span>5</span><span>)</span>\n <span>WITHIN</span> <span>GROUP</span> <span>(</span><span>ORDER</span> <span>BY</span> <span>peak_memory_usage_bytes</span><span>)</span>\n <span>AS</span> <span>median</span>\n <span>FROM</span> <span>key_performance_indicators</span>\n <span>GROUP</span> <span>BY</span> <span>app_version</span><span>;</span>\n <span>-- app_version \u2502 median</span>\n <span>-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550</span>\n <span>-- &quot;1.0.1&quot; \u2502 192500000</span>\n <span>-- &quot;1.0.0&quot; \u2502 204800000</span>\n </code></pre>\n<aside>\n<p>PostgreSQL doesn\u2019t handle camelcase well for table or column names,\nso keep that in mind when using functions like\n<a rel=\"noopener noreferrer\" href=\"https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING-TABLE\"><code>jsonb_to_record</code></a>.</p>\n</aside>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#creating-a-web-service\"></a>Creating a Web Service</h3>\n<p>In this example,\nmost of the heavy lifting is delegated to Postgres,\nmaking the server-side implementation rather boring.\nFor completeness,\nhere are some reference implementations in\nRuby (Sinatra) and JavaScript (Express):</p>\n<div>\n<div>\n\n\n</div>\n<pre data-lang=\"Ruby\" tabindex=\"0\"><code><span>require</span> <span>'sinatra/base'</span>\n <span>require</span> <span>'pg'</span>\n <span>require</span> <span>'sequel'</span>\n <span>class</span> <span>App</span> <span>&lt;</span> <span>Sinatra</span><span>::</span><span>Base</span>\n <span>configure</span> <span>do</span>\n <span>DB</span> <span>=</span> <span>Sequel</span><span>.</span><span>connect</span><span>(</span><span>ENV</span><span>[</span><span>'DATABASE_URL'</span><span>])</span>\n <span>end</span>\n <span>post</span> <span>'/collect'</span> <span>do</span>\n <span>DB</span><span>[</span><span>:metrics</span><span>].</span><span>insert</span><span>(</span><span>payload: </span><span>request</span><span>.</span><span>body</span><span>.</span><span>read</span><span>)</span>\n <span>status</span> <span>204</span>\n <span>end</span>\n <span>end</span>\n </code></pre>\n<pre data-lang=\"JavaScript\" hidden=\"hidden\" tabindex=\"0\"><code><span>import</span> <span>express</span> <span>from</span> <span>'express'</span><span>;</span>\n <span>import</span> <span>{</span> <span>Pool</span> <span>}</span> <span>from</span> <span>'pg'</span><span>;</span>\n <span>const</span> <span>db</span> <span>=</span> <span>new</span> <span>Pool</span><span>(</span>\n <span>connection<wbr>String</span><span>:</span> <span>process</span><span>.</span><span>env</span><span>.</span><span>DATABASE_URL</span><span>,</span>\n <span>ssl</span><span>:</span> <span>process</span><span>.</span><span>env</span><span>.</span><span>NODE_ENV</span> <span>===</span> <span>'production'</span>\n <span>);</span>\n <span>const</span> <span>app</span> <span>=</span> <span>express</span><span>();</span>\n <span>app</span><span>.</span><span>post</span><span>(</span><span>'/collect'</span><span>,</span> <span>(</span><span>request</span><span>,</span> <span>response</span><span>)</span> <span>=></span> <span>{</span>\n <span>db</span><span>.</span><span>query</span><span>(</span><span>'INSERT INTO metrics (payload) VALUES ($1)'</span><span>,</span> <span>[</span><span>request</span><span>.</span><span>body</span><span>],</span> <span>(</span><span>error</span><span>,</span> <span>results</span><span>)</span> <span>=></span> <span>{</span>\n <span>if</span> <span>(</span><span>error</span><span>)</span> <span>{</span>\n <span>throw</span> <span>error</span><span>;</span>\n <span>}</span>\n <span>response</span><span>.</span><span>status</span><span>(</span><span>204</span><span>);</span>\n <span>})</span>\n <span>});</span>\n <span>app</span><span>.</span><span>listen</span><span>(</span><span>process</span><span>.</span><span>env</span><span>.</span><span>PORT</span> <span>||</span> <span>5000</span><span>)</span>\n </code></pre>\n</div>\n<h3>\n<a href=\"https://nshipster.com/metrickit/#sending-metrics-as-json\"></a>Sending Metrics as JSON</h3>\n<p>Now that we have everything set up,\nthe final step is to implement\nthe required <code>MXMetric<wbr>Manager<wbr>Subscriber</code> delegate method <code>did<wbr>Receive(_:)</code>\nto pass that information along to our web service:</p>\n<pre data-lang=\"Swift\"><code><span>extension</span> <span>App<wbr>Delegate</span><span>:</span> <span>MXMetric<wbr>Manager<wbr>Subscriber</span> <span>{</span>\n <span>func</span> <span>did<wbr>Receive</span><span>(</span><span>_</span> <span>payloads</span><span>:</span> <span>[</span><span>MXMetric<wbr>Payload</span><span>])</span> <span>{</span>\n <span>for</span> <span>payload</span> <span>in</span> <span>payloads</span> <span>{</span>\n <span>let</span> <span>url</span> <span>=</span> <span>URL</span><span>(</span><span>string</span><span>:</span> <span>&quot;https://example.com/collect&quot;</span><span>)</span><span>!</span>\n <span>var</span> <span>request</span> <span>=</span> <span>URLRequest</span><span>(</span><span>url</span><span>:</span> <span>url</span><span>)</span>\n <span>request</span><span>.</span><span>http<wbr>Method</span> <span>=</span> <span>&quot;POST&quot;</span>\n <span>request</span><span>.</span><span>http<wbr>Body</span> <span>=</span> <span>payload</span><span>.</span><span>json<wbr>Representation</span><span>()</span>\n <span>let</span> <span>task</span> <span>=</span> <span>URLSession</span><span>.</span><span>shared</span><span>.</span><span>data<wbr>Task</span><span>(</span><span>with</span><span>:</span> <span>request</span><span>)</span>\n <span>task</span><span>.</span><span>priority</span> <span>=</span> <span>URLSession<wbr>Task</span><span>.</span><span>low<wbr>Priority</span>\n <span>task</span><span>.</span><span>resume</span><span>()</span>\n <span>}</span>\n <span>}</span>\n <span>}</span>\n </code></pre>\n<hr>\n<p>When you create something and put it out into the world,\nyou lose your direct connection to it.\nThat\u2019s as true for apps as it is for college radio shows.\nShort of user research studies or\n<a rel=\"noopener noreferrer\" href=\"https://techcrunch.com/2019/02/06/iphone-session-replay-screenshots/\">invasive ad-tech</a>,\nthe truth is that\n<a rel=\"noopener noreferrer\" href=\"https://xkcd.com/1172/\">we rarely have any clue about how people are using our software</a>.</p>\n\n<p>Metrics offer a convenient way to at least make sure that\nthings aren\u2019t too slow or too draining.\nAnd though they provide but a glimpse in the aggregate\nof how our apps are being enjoyed,\nit\u2019s just enough to help us honor both our creation and our audience\nwith a great user experience.</p>\n\n<img width=\"1\" alt=\"\" src=\"http://feeds.feedburner.com/~r/NSHipster/~4/o2-j6xKjBrA\" height=\"1\">"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/06e5FJWgUfUSmDaPJIEZoGF1XOs=/0x68:2040x1136/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/10378819/DSCF3031.jpg",
"width": 1200,
"height": 628,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 4744,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/e31b3fcb-27f6-4f3e-b96c-53902586e366",
"label": "Weblogs"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572500250190
},
{
"originId": "https://inessential.com/2019/10/22/netnewswire_5_0_3_for_mac_released",
"fingerprint": "2c4d6d88",
"id": "+jHfsXnBCVfCstSIW1WDumAyigT4rnsUPnI5WFxgnAU=_16df54dfcca:13322:d4506071",
"summary": {
"direction": "ltr",
"content": "<p><a href=\"https://ranchero.com/netnewswire/\"><img width=\"256\" alt=\"NetNewsWire for Mac icon: globe with a satellite in the foreground.\" src=\"https://ranchero.com/images/nnw_icon_256.png\" height=\"256\"></a></p>\n<p>The main things in this release are 1) enhanced performance and 2) importing subscriptions from NetNewsWire 3 (since it won\u2018t run on Catalina).</p>\n<p>There are also a bunch of bug fixes \u2014 including a fix for the space bar behavior on Catalina \u2014 and there\u2019s a new feature: you can type the <code>s</code> key to star and unstar an article.</p>\n<p>For more details, <a href=\"https://nnw.ranchero.com/2019/10/22/netnewswire-for-mac.html\">read the change notes</a> on the NetNewsWire blog.</p>"
},
"alternate": [
{
"href": "https://inessential.com/2019/10/22/netnewswire_5_0_3_for_mac_released",
"type": "text/html"
}
],
"crawled": 1571778591946,
"title": "NetNewsWire 5.0.3 for Mac Released",
"published": 1571775505000,
"origin": {
"streamId": "feed/http://ranchero.com/xml/rss.xml",
"htmlUrl": "https://inessential.com/",
"title": "inessential.com"
},
"visual": {
"url": "http://www.blogcdn.com/www.engadget.com/media/2013/10/nvidia-shield-console-mode.jpg",
"width": 620,
"height": 340,
"contentType": "image/jpg"
},
"unread": false,
"readTime": 5621,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572500226675
},
{
"id": "AxO6mug+YPRclcA3EJcsykvvS1qcjXH62IXONGWCBII=_16db2add61b:afd:a4acdac",
"originId": "58495.pz39s0 at https://www.imore.com",
"fingerprint": "8fe463a6",
"content": {
"content": "<div><div><div><p>An Apple Support rep apparently said, &quot;I do not know how this could of happened.&quot;</p>\n<p><a href=\"https://www.imore.com/apple-card-user-says-he-was-victim-fraud\" title=\"Apple Card user says he was the victim of fraud\"><img src=\"https://www.imore.com/sites/imore.com/files/styles/large_wm_blw/public/field/image/2019/08/apple-card-hero-05.jpeg?itok=IWclhnvc\"></a></p>\n<h2>What you need to know</h2>\n<ul><li>An Apple Card user claims he was the victim of fraud.</li>\n<li>When he contacted Apple Support, they said, &quot;I do not know how this could of happened.&quot;</li>\n<li>Apple Card touts an extra level of security with no numbers and no CVV.</li>\n</ul><p>When Apple Card debuted, one of its biggest draws was Apple's focus on security. On Apple's website, it says, &quot;It's hard to steal a credit card number when you can't see it.&quot; But that's apparently what happened to one Apple Card user who reached out to <a href=\"https://9to5mac.com/2019/10/09/apple-card-security-cloning/\">9to5Mac</a>, claiming they were the victim of fraud.</p>\n<p>The Apple Card user said they reached out to Apple Support and received this response:</p>\n<blockquote>\n<p>I do not know how this could of happened. It's very rare for your card to be in two places at one time. Since our physical cards have no number on it, it's very hard for someone to copy it.</p>\n</blockquote>\n<p>The Apple Card user confirmed the fraudulent charge after receiving an alert on his iPhone. The tricky thing is the purchase was apparently labeled as being nearby, but clicking on the map revealed it was hours away, 9to5Mac explained.</p>\n<p>On Apple's website, the company highlights the fact that the Apple Card doesn't have any numbers on it. &quot;Not even a CVV. So that's one less thing to worry about when you hand over your card at a restaurant or store.&quot; But that doesn't guarantee it can't be stolen.</p>\n<p>9to5Mac speculates that the Apple Card user may have been the victim of skimming, which can potentially affect all credit cards and debit cards. It's a reminder to be extra vigilant when swiping your card at a gas station or ATM. Better yet, use Apple Pay when possible.</p>\n<p></p><p></p>\n<div>\n<h3><a href=\"https://www.imore.com/apple-goldman-sachs-credit-card\">Apple Card</a></h3>\n<p><a href=\"https://www.imore.com/apple-goldman-sachs-credit-card\"></a><a href=\"https://www.imore.com/apple-card-user-says-he-was-victim-fraud\" title=\"Apple Card user says he was the victim of fraud\"><img src=\"https://www.imore.com/sites/imore.com/files/styles/large/public/field/image/2019/03/apple-card-iphone-xs-payment.jpg\"></a></p>\n<ul><li><a href=\"https://www.imore.com/apple-goldman-sachs-credit-card\">Apple Card: Everything you need to know</a></li>\n<li><a href=\"https://www.imore.com/amex-business-gold-card-apple-store-rewards\">Best Apple Store rewards card</a></li>\n<li><a href=\"https://www.imore.com/apple-card-missing-these-huge-signup-bonuses\">Best credit cards with sign up bonuses</a></li>\n<li><a href=\"https://www.imore.com/not-approved-apple-card-try-applying-these-cards-instead\">What to do if you're not approved for Apple Card</a></li>\n<li><a href=\"https://www.imore.com/is-apple-card-worth-getting\">Is Apple Card worth getting?</a></li>\n</ul></div>\n<p></p></div></div></div><img width=\"1\" alt=\"\" src=\"http://feeds.feedburner.com/~r/TheIphoneBlog/~4/DzBrQNf9PCU\" height=\"1\">",
"direction": "ltr"
},
"title": "Apple Card user says he was the victim of fraud",
"author": "Brandon Russell",
"summary": {
"content": "An Apple Support rep apparently said, &quot;I do not know how this could of happened.&quot;\nWhat you need to know\nAn Apple Card user claims he was the victim of fraud.\nWhen he contacted Apple Support, they said, &quot;I do not know how this could of happened.&quot;\nApple Card touts an extra level of security with no numbers and no CVV.\nWhen Apple Card debuted, one of its biggest draws was Apple's focus on security. On Apple's website, it says, &quot;It's hard to steal a credit card number when you can't see it.&quot; But that's apparently what happened to one Apple Card user who reached out to 9to5Mac, claiming they were the victim of fraud.\nThe Apple Card user said they reached out to Apple Support and received this response:\nI do not know how this could of happened. It's very rare for your card to be in two places at one time. Since our physical cards have no number on it, it's very hard for someone to copy it.\nThe Apple Card user confirmed the fraudulent charge after receiving an alert on his iPhone. ...",
"direction": "ltr"
},
"alternate": [
{
"href": "http://feeds.imore.com/~r/TheIphoneBlog/~3/DzBrQNf9PCU/apple-card-user-says-he-was-victim-fraud",
"type": "text/html"
}
],
"canonical": [
{
"href": "https://www.imore.com/apple-card-user-says-he-was-victim-fraud",
"type": "text/html"
}
],
"crawled": 1570660800027,
"published": 1570660313000,
"origin": {
"streamId": "feed/http://www.imore.com/rss.xml",
"title": "iMore - The #1 iPhone, iPad, and iPod touch blog",
"htmlUrl": "https://www.imore.com/"
},
"unread": false,
"readTime": 2216,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572500208603
},
{
"keywords": [
"Development",
"How To",
"Xcode"
],
"originId": "https://ericasadun.com/?p=6502",
"fingerprint": "878bb7e8",
"id": "eos9yhbovnwyH+gNY6NsrxoZIGI+1zv2KFTRDDBJycA=_16e0e491282:185ad:d4506071",
"author": "erica",
"summary": {
"direction": "ltr",
"content": "Most Xcode users quickly become familiar with the basics of the Find Navigator panel. With it, you can find text, regular expressions, and perform search-and-replace, whether matching or ignoring case. But that\u2019s just scratching the surface of the Find Navigator. I thought I\u2019d drop a few words today about search scopes. Controlled from the bottom [\u2026]"
},
"alternate": [
{
"href": "https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/",
"type": "text/html"
}
],
"crawled": 1572197700226,
"title": "Fun with Xcode Search Domains: Excluding match text",
"published": 1572195876000,
"origin": {
"streamId": "feed/http://ericasadun.com/feed/",
"htmlUrl": "https://ericasadun.com",
"title": "Erica Sadun"
},
"content": {
"direction": "ltr",
"content": "<p>Most Xcode users quickly become familiar with the basics of the Find Navigator panel.</p>\n<p><img data-orig-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.32.34-AM.png?fit=273%2C104&amp;ssl=1\" data-orig-size=\"273,104\" data-medium-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.32.34-AM.png?fit=273%2C104&amp;ssl=1\" data-attachment-id=\"6503\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-description=\"\" src=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.32.34-AM.png?resize=273%2C104&ssl=1\" data-permalink=\"https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/screen-shot-2019-10-27-at-10-32-34-am/\" data-large-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.32.34-AM.png?fit=273%2C104&amp;ssl=1\" alt=\"\" data-image-title=\"Screen Shot 2019-10-27 at 10.32.34 AM\" width=\"273\" data-comments-opened=\"1\" class=\"wp-image-6503\" data-recalc-dims=\"1\" height=\"104\"></p>\n<p>With it, you can find text, regular expressions, and perform search-and-replace, whether matching or ignoring case. But that\u2019s just scratching the surface of the Find Navigator.</p>\n<p>I thought I\u2019d drop a few words today about search scopes. Controlled from the bottom left, \u00a0under the search field, you can create narrowed searches. This enables you to, for example, search only in Swift files or exclude files containing the word Test.</p>\n<p>To get started, click the icon (two lines with three squares on a line between them) and then New Scope (the plus icon). Here, you can name the scope, limit the search extent, and add criteria for exactly which files should be included or not.</p>\n<p><img data-orig-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?fit=863%2C223&amp;ssl=1\" data-orig-size=\"863,223\" data-medium-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?fit=300%2C78&amp;ssl=1\" data-attachment-id=\"6504\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-description=\"\" src=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?resize=863%2C223&ssl=1\" data-permalink=\"https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/screen-shot-2019-10-27-at-10-37-57-am/\" data-large-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?fit=863%2C223&amp;ssl=1\" alt=\"\" data-image-title=\"Screen Shot 2019-10-27 at 10.37.57 AM\" srcset=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?w=863&amp;ssl=1 863w, https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?resize=300%2C78&amp;ssl=1 300w, https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?resize=768%2C198&amp;ssl=1 768w, https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.37.57-AM.png?resize=535%2C138&amp;ssl=1 535w\" sizes=\"(max-width: 863px) 100vw, 863px\" width=\"863\" data-comments-opened=\"1\" class=\"wp-image-6504\" data-recalc-dims=\"1\" height=\"223\"></p>\n<p>The logic is straightforward. You choose where to look (the project, a folder, or through the entire SDK), and whether to include all conditions or some conditions:</p>\n<p><img data-orig-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?fit=310%2C105&amp;ssl=1\" data-orig-size=\"310,105\" data-medium-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?fit=300%2C102&amp;ssl=1\" data-attachment-id=\"6505\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-description=\"\" src=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?resize=310%2C105&ssl=1\" data-permalink=\"https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/screen-shot-2019-10-27-at-10-39-49-am/\" data-large-file=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?fit=310%2C105&amp;ssl=1\" alt=\"\" data-image-title=\"Screen Shot 2019-10-27 at 10.39.49 AM\" srcset=\"https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?w=310&amp;ssl=1 310w, https://i2.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.49-AM.png?resize=300%2C102&amp;ssl=1 300w\" sizes=\"(max-width: 310px) 100vw, 310px\" width=\"310\" data-comments-opened=\"1\" class=\"wp-image-6505\" data-recalc-dims=\"1\" height=\"105\"></p>\n<p>Each condition is based on the file name, path, extension, UTI (the kind of file, like image which is useful for finding vector assets), Workspace location (namely groups), or source control status (handy for finding newly applied changes.)</p>\n<p>Most of my conditions are file-name-based. And for those, you get the following matching conditions. The \u201cends with\u201d is an obvious win for extensions (although you can also use UTIs for that), and \u201cstarts with\u201d can help for projects organized in hierarchical ways.</p>\n<p><img data-orig-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?fit=484%2C113&amp;ssl=1\" data-orig-size=\"484,113\" data-medium-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?fit=300%2C70&amp;ssl=1\" data-attachment-id=\"6506\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-description=\"\" src=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?resize=484%2C113&ssl=1\" data-permalink=\"https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/screen-shot-2019-10-27-at-10-39-44-am/\" data-large-file=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?fit=484%2C113&amp;ssl=1\" alt=\"\" data-image-title=\"Screen Shot 2019-10-27 at 10.39.44 AM\" srcset=\"https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?w=484&amp;ssl=1 484w, https://i0.wp.com/ericasadun.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-27-at-10.39.44-AM.png?resize=300%2C70&amp;ssl=1 300w\" sizes=\"(max-width: 484px) 100vw, 484px\" width=\"484\" data-comments-opened=\"1\" class=\"wp-image-6506\" data-recalc-dims=\"1\" height=\"113\"></p>\n<p>Now, interestingly enough, this list fails to offer \u201cdoes not contain\u201d but that\u2019s fairly easy to work around. Since Xcode supports regex matching, you can easily replicate \u201cdoes not contain\u201d with an <a href=\"https://www.regextester.com/15\">appropriate regex</a>:</p>\n<p><a href=\"https://ericasadun.com/wp-content/uploads/2019/10/AntiMatch.tiff\"><img data-orig-file=\"https://ericasadun.com/wp-content/uploads/2019/10/AntiMatch.tiff\" data-orig-size=\"\" data-medium-file=\"https://ericasadun.com/wp-content/uploads/2019/10/AntiMatch.tiff\" data-attachment-id=\"6507\" data-image-meta=\"[]\" data-image-description=\"\" src=\"https://ericasadun.com/wp-content/uploads/2019/10/AntiMatch.tiff\" data-permalink=\"https://ericasadun.com/2019/10/27/fun-with-xcode-search-domains-excluding-match-text/antimatch/\" data-large-file=\"https://ericasadun.com/wp-content/uploads/2019/10/AntiMatch.tiff\" alt=\"\" data-image-title=\"AntiMatch\" data-comments-opened=\"1\" class=\"wp-image-6507\"></a></p>\n<p>Change the file name to a path to exclude source file directories.</p>\n<p>You can create as many search domains as you like. At least, I haven\u2019t found an upper bound yet. I haven\u2019t found a way to reorder the find scopes, although if you\u2019re really controlling about this, you can pop into \u00a0your workspace (<code>ProjectName.xcodeproj/project.xcworkspace/xcuserdata/username.xcuserdatad</code>), convert your <code>UserInterfaceState.xcuserstate</code> to xml (<code>plutil -convert xml1</code>), and hand-edit it the way you need.</p>\n<p>There are lots of wonderful little Xcode tweaks like these throughout this monster of an IDE. What are some of your faves? If I have time this week, I\u2019ll share some of mine, such as the four-square \u2014 another of my favorite tools \u2014 and a few great ways to connect your editor to the navigator.</p>"
},
"unread": false,
"readTime": 3406,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1572499929438
},
{
"keywords": [
"Apple"
],
"originId": "https://9to5mac.com/?p=617536",
"recrawled": 1572455791719,
"updateCount": 1,
"fingerprint": "77224d8",
"id": "BmoAzSEWHFzR01wyxBZAhNEo11Vy8oDR1qKDe+tKVEQ=_16e1d065ad9:1c0f1:d4506071",
"author": "Michael Potuck",
"summary": {
"direction": "ltr",
"content": "<div><img src=\"https://9to5mac.com/wp-content/uploads/sites/6/2019/10/sonos-discount-program-trade-in.jpeg?quality=82&strip=all&w=1600\"></div>\n<p>Sonos has announced a new initiative today that makes it easy for existing customers to trade in older Sonos products for a nice discount on new ones. For Apple customers, the <a href=\"http://www.anrdoezrs.net/links/9173227/type/dlg/https://www.sonos.com/en-us/tradeup\">Trade Up program</a> is a neat opportunity to bring <a href=\"http://www.anrdoezrs.net/links/9173227/type/dlg/https://www.sonos.com/en-us/products/wireless-speakers\">Sonos\u2019 AirPlay 2 compatible speakers</a> into your home.</p>\n<p> <a href=\"https://9to5mac.com/2019/10/30/sonos-trade-in-program-airplay-2/#more-617536\">more\u2026</a></p>\n<p>The post <a rel=\"nofollow\" href=\"https://9to5mac.com/2019/10/30/sonos-trade-in-program-airplay-2/\">Sonos pushing AirPlay 2 speaker lineup with new Trade Up program</a> appeared first on <a rel=\"nofollow\" href=\"https://9to5mac.com\">9to5Mac</a>.</p>"
},
"alternate": [
{
"href": "https://9to5mac.com/2019/10/30/sonos-trade-in-program-airplay-2/",
"type": "text/html"
}
],
"crawled": 1572444986073,
"title": "Sonos pushing AirPlay 2 speaker lineup with new Trade Up program",
"published": 1572442686000,
"origin": {
"streamId": "feed/http://9to5mac.com/feed/",
"htmlUrl": "https://9to5mac.com",
"title": "9to5Mac"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/06e5FJWgUfUSmDaPJIEZoGF1XOs=/0x68:2040x1136/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/10378819/DSCF3031.jpg",
"width": 1200,
"height": 628,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 4493,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499889443
},
{
"originId": "tag:blogger.com,1999:blog-8954608646904080796.post-4991449931465752891",
"fingerprint": "7d99be14",
"thumbnail": [
{
"url": "https://1.bp.blogspot.com/-rGjQOQb89a8/XbS25YYbgeI/AAAAAAAADUI/s8Q4e0pTtX4qSTP3wrkt13VGV53Q8J0tQCLcBGAsYHQ/s72-c/092.jpg",
"width": 72,
"height": 72
}
],
"id": "v0v+7Ya8tssIZvd3/pcnFRr3HwvY/5YK3FGc2t65c0Y=_16e0a232509:17e32:d4506071",
"updated": 1572124724480,
"author": "Edward Feser",
"alternate": [
{
"href": "http://edwardfeser.blogspot.com/2019/10/john-paul-ii-in-defense-of-nation-and.html",
"type": "text/html"
}
],
"crawled": 1572128105737,
"title": "John Paul II in defense of the nation and patriotism",
"published": 1572124680000,
"origin": {
"streamId": "feed/http://edwardfeser.blogspot.com/feeds/posts/default",
"htmlUrl": "http://edwardfeser.blogspot.com/",
"title": "Edward Feser"
},
"content": {
"direction": "ltr",
"content": "<br><div><a href=\"https://1.bp.blogspot.com/-rGjQOQb89a8/XbS25YYbgeI/AAAAAAAADUI/s8Q4e0pTtX4qSTP3wrkt13VGV53Q8J0tQCLcBGAsYHQ/s1600/092.jpg\"><img border=\"0\" data-original-height=\"180\" src=\"https://1.bp.blogspot.com/-rGjQOQb89a8/XbS25YYbgeI/AAAAAAAADUI/s8Q4e0pTtX4qSTP3wrkt13VGV53Q8J0tQCLcBGAsYHQ/s1600/092.jpg\" data-original-width=\"144\"></a></div><div><span>In chapters 11-15 of his last book <span><i><a href=\"https://www.amazon.com/Memory-Identity-Conversations-Dawn-Millennium/dp/0847827615/ref=sr_1_1?keywords=john+paul+II+memory+and+identity&qid=1572031154&sr=8-1\">Memory and Identity</a></i></span>, Pope St. John Paul II provides a lucid exposition of the idea of the nation as a natural social institution and of the virtue of patriotism, as these have been understood in traditional natural law theory and Catholic moral theology.\u00a0 The relevance to current controversies will be obvious.</span></div><div><span><br></span></div><div><span>What is the nation, and what is patriotism?<span>\u00a0 </span>John Paul begins by noting the connection between the nation and the family, where the former is in a sense an extension of the latter:</span></div><a name=\"more\"></a><br> <div><i><span>The Latin word </span></i><span>patria<i> is associated with the idea and the reality of \u201cfather\u201d (</i>pater<i>).<span>\u00a0 </span>The native land (or fatherland) can in some ways be identified with patrimony \u2013 that is, the totality of goods bequeathed to us by our forefathers\u2026 Our native land is thus our heritage and it is also the whole patrimony derived from that heritage.<span>\u00a0 </span>It refers to the land, the territory, but more importantly, the concept of patria includes the values and spiritual content that make up the culture of a given nation</i>. (p. 60)</span></div><div><span><br></span></div><div><span>As that last remark makes clear, the ties of blood are less important than those of culture.<span>\u00a0 </span>Indeed, multiple ethnicities can make up a nation.<span>\u00a0 </span>Referring to his native Poland, the pope notes that \u201cin ethnic terms, perhaps the most significant event for the foundation of the nation was the union of two great tribes,\u201d and yet other peoples too eventually went on together to comprise \u201cthe Polish nation\u201d (p. 77).<span>\u00a0 </span>It is shared culture, and especially a shared religion, that formed these diverse ethnicities into a nation:</span></div><div><span><br></span></div><div><i><span>When we speak of Poland\u2019s baptism, we are not simply referring to the sacrament of Christian initiation received by the first historical sovereign of Poland, but also to the event which was decisive for the birth of the nation and the formation of its Christian identity.<span>\u00a0 </span>In this sense, the date of Poland\u2019s baptism marks a turning point.<span>\u00a0 </span>Poland as a nation emerges from its prehistory at that moment and begins to exist in history</span></i><span>.<span>\u00a0 </span>(p. 77)</span></div><div><span><br></span></div><div><span>That a shared culture is the key to understanding the nation is a theme John Paul emphasizes repeatedly throughout the book.<span>\u00a0 </span>He says that \u201cevery nation draws life from the works of its own culture\u201d (p. 83), and that:</span></div><div><span><br></span></div><div><i><span>The nation is, in fact, the great community of men who are united by various ties, but above all, precisely by culture.<span>\u00a0 </span>The nation exists\u00a0<span>\u2018through\u2019 culture and \u2018for\u2019 culture\u00a0</span>and it is therefore the great educator of men in order that they may \u2018be more\u2019 in the community\u2026</span></i></div><div><i><span><br></span></i></div><div><i><span>I am the son of a nation which\u2026 has kept its identity, and it has kept, in spite of partitions and foreign occupations, its national sovereignty, not by relying on the resources of physical power but solely\u00a0<span>by relying on its culture. </span><span>\u00a0</span>This culture turned out, under the circumstances, to be more powerful than all other forces.<span>\u00a0 </span>What I say here concerning the right of the nation to the foundation of its culture and its future is not, therefore, the echo of any \u2018nationalism\u2019, but it is always a question of a stable element of human experience and of the\u00a0<span>humanistic perspective of man's development.<span>\u00a0 </span></span>There exists a fundamental sovereignty of society, which is manifested in the culture of the nation</span></i><span>. (p. 85)</span></div><div><span><br></span></div><div><span>In addition to shared values and religion, John Paul identifies shared history as another crucial aspect of a nation\u2019s identifying culture:</span></div><div><span><br></span></div><div><i><span>Like individuals, then, nations are endowed with historical memory\u2026 And the histories of nations, objectified and recorded in writing, are among the essential elements of culture \u2013 the element which determines the nation\u2019s identity in the temporal dimension</span></i><span>.<span>\u00a0 </span>(pp. 73-74)</span></div><div><span><br></span></div><div><span>The pope notes that citizens of modern Western European countries often have \u201creservations\u201d about the notion of \u201cnational identity as expressed through culture,\u201d and have even \u201carrived at a stage which could be defined as \u2018post-identity\u2019\u201d (p. 86).<span>\u00a0 </span>There is \u201ca widespread tendency to move toward supranational structures, even internationalism\u201d with \u201csmall nations\u2026 allow[ing] themselves to be absorbed into larger political structures\u201d (p. 66).<span>\u00a0 </span>However, the disappearance of the nation would be contrary to the natural order of things:</span></div><div><span><br></span></div><div><i><span>Yet it still seems that nation and native land, like the family, are permanent realities.<span>\u00a0 </span>In this regard, Catholic social doctrine speaks of \u201cnatural\u201d societies, indicating that both the family and the nation have a particular bond with human nature, which has a social dimension.<span>\u00a0 </span>Every society\u2019s formation takes place in and through the family: of this there can be no doubt.<span>\u00a0 </span>Yet something similar could also be said about the nation</span></i><span>. (p. 67)</span></div><div><span><br></span></div><div><span>And again:</span></div><div><span><br></span></div><div><i><span>The term \u201cnation\u201d designates a community based in a given territory and distinguished from other nations by its culture.<span>\u00a0 </span>Catholic social doctrine holds that the family and the nation are both natural societies, not the product of mere convention.<span>\u00a0 </span>Therefore, in human history they cannot be replaced by anything else.<span>\u00a0 </span>For example, the nation cannot be replaced by the State, even though the nation tends naturally to establish itself as a State\u2026 Still less is it possible to identify the nation with so-called democratic society, since here it is a case of two distinct, albeit interconnected orders.<span>\u00a0 </span>Democratic society is closer to the State than is the nation.<span>\u00a0 </span>Yet the nation is the ground on which the State is born</span></i><span>. (pp. 69-70)</span></div><div><span><br></span></div><div><span>As this last point about the state and democracy indicates, a nation cannot be defined in terms of, or replaced by, either governmental institutions and their laws and policies on the one hand, or the aggregate of the attitudes of individual citizens on the other.<span>\u00a0 </span>It is something deeper than, and presupposed by, both of these things.<span>\u00a0 </span>It is only insofar as a nation, defined by its culture, is already in place that a polity can come into being.<span>\u00a0 </span>Hence it is a mistake to think that, if the common cultural bonds that define a nation disappear, the nation can still be held together by virtue of governmental policy either imposed from above or arrived at my majority vote.<span>\u00a0 </span>For a people have to be united by common bonds of culture before they can all see either governmental policy or the will of the majority as legitimate.<span>\u00a0 </span>(Readers familiar with the work of Roger Scruton will note the parallels, and how deeply conservative John Paul II\u2019s understanding of the nation is.)<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Now, as a natural institution, the nation, like the family, is necessary for our well-being.<span>\u00a0 </span>And as with the family, this entails a <i>moral duty</i> to be loyal to and to defend one\u2019s nation \u2013 and for precisely the same sorts of reasons one has a duty of loyalty to and defense of one\u2019s family:</span></div><div><span><br></span></div><div><i><span>If we ask where patriotism appears in the Decalogue, the reply comes without hesitation: it is covered by the fourth commandment, which obliges us to honor our father and mother.<span>\u00a0 </span>It is included under the umbrella of the Latin word </span></i><span>pietas<i>, which underlines the religious dimension of the respect and veneration due to parents\u2026</i></span></div><div><span><i><br></i></span></div><div><i><span>Patriotism is a love for everything to do with our native land: its history, its traditions, its language, its natural features.<span>\u00a0 </span>It is a love which extends also to the works of our compatriots and the fruits of their genius.<span>\u00a0 </span>Every danger that threatens the overall good of our native land becomes an occasion to demonstrate this love</span></i><span>. (pp. 65-66)</span></div><div><span><br></span></div><div><span>Among the dangers to the nation are the opposite extreme economic errors of egalitarian statism and liberal individualism, which threaten to destroy the common culture that defines the nation \u2013 in the one case from the top down and in the other from the bottom up.<span>\u00a0 </span>The pope writes:</span></div><div><span><br></span></div><div><i><span>[W]e must ask how best to respect the proper relationship between economics and culture without destroying this greater human good for the sake of profit, in deference to the overwhelming power of one-sided market forces.<span>\u00a0 </span>It matters little, in fact, whether this kind of tyranny is imposed by Marxist totalitarianism or by Western liberalism</span></i><span>. (pp. 83-84)</span></div><div><span><br></span></div><div><span>If liberal individualism is an error that pays insufficient respect to the nation, there is of course an opposite extreme error which involves giving excessive esteem to the nation \u2013 namely, nationalism.<span>\u00a0 </span>Patriotism, rightly understood, is the middle ground between these extremes:</span></div><div><span><br></span></div><div><i><span>Whereas nationalism involves recognizing and pursuing the good of one\u2019s own nation alone, without regard for the rights of others, patriotism, on the other hand, is a love for one\u2019s native land that accords rights to all other nations equal to those claimed for one\u2019s own</span></i><span>. (p. 67)</span></div><div><span><br></span></div><div><span>John Paul II was clear that the remedy for nationalism was not to go to the opposite extreme (whether in the name of individualism, internationalism, or whatever), but rather precisely to insist on the sober middle ground:</span></div><div><span><br></span></div><div><i><span>How can we be delivered from such a danger?<span>\u00a0 </span>I think the right way is through patriotism\u2026 Patriotism, in other words, leads to a properly ordered social love</span></i><span>. (p. 67)</span></div><div><span><br></span></div><div><span>Now, let\u2019s note a number of things about these remarks and their implications.<span>\u00a0 </span>First, as I have said, what the late pope was giving expression to here is not merely his personal opinion, but traditional natural law political philosophy and Catholic moral teaching \u2013 the kind of thing that would have been well known to someone formed in Thomistic philosophy and theology in the early twentieth century, as John Paul II was.</span></div><div><span><br></span></div><div><span>Second, John Paul\u2019s teaching implies that those who seek to preserve their nation\u2019s common culture, and for that reason are concerned about trends that might radically alter its religious makeup or undermine its common language and reverence for its history, are simply following a natural and healthy human impulse and indeed following out the implications of the fourth commandment.<span>\u00a0 </span>There is no necessary connection between this attitude and racism, hatred for immigrants, religious bigotry, or the like.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Of course, a person who seeks to preserve his nation\u2019s culture <i>might</i> also be a racist or xenophobe or bigot.<span>\u00a0 </span>The point, however, is that he <i>need not</i> be, and indeed that it is wrong even to <i>presume</i> that he is, because a special love for one\u2019s own nation and desire to preserve its culture is a natural human tendency, and thus likely to be found even in people who have no racist or xenophobic or bigoted attitudes at all.<span>\u00a0 </span>Indeed, it is, again, even morally virtuous.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Needless to say, there is <i>also</i> a moral need to balance this patriotism with a welcoming attitude toward immigrants, with respect for the rights of religious minorities, and so forth.<span>\u00a0 </span>The point, however, is that <i>all </i>of these things need to be balanced. <span>\u00a0</span>Too many contemporary Catholics, including some churchmen, have a tendency to emphasize only the latter while ignoring the former.<span>\u00a0 </span>They have a tendency to buy into the leftist narrative according to which the current wave of populist and patriotic sentiment in the United States and Western Europe is merely an expression of racism and xenophobia.<span>\u00a0 </span>This is deeply unjust, contrary to Catholic teaching, and politically dangerous.<span>\u00a0 </span>It is unjust and contrary to Catholic teaching because, again, both natural law and traditional moral theology affirm that a desire to preserve one\u2019s nation and its culture are natural human sentiments and morally praiseworthy.<span>\u00a0 </span>It is dangerous because, when governing authorities fail to respect and take account of these natural and decent human sentiments, they are <i>inviting</i> rather than preventing a nationalist overreaction. </span></div><div><span><br></span></div><div><span>(President Trump has famously called himself a \u201cnationalist,\u201d which is unfortunate given the connotations of that term.<span>\u00a0 </span>However, from <span><a href=\"https://www.nbcnews.com/politics/donald-trump/future-does-not-belong-globalists-trump-pushes-nationalism-u-n-n1058171\">his 2019 address to the United Nations</a></span> it seems clear that what he means by this is just the defense of the institution of the nation against those who would dissolve it in the name of globalism, open borders, etc.<span>\u00a0 </span>Moreover, he explicitly affirmed the right of <i>every</i> nation to preserve itself and its sovereignty, and the right of <i>every</i>human being to have a special patriotic love and preference for his own country.<span>\u00a0 </span>He also has repeatedly called for the United States to refrain from intervening in the affairs of other nations.<span>\u00a0 </span>So it is evident that it is really just <i>patriotism</i> in the sense described above, rather than some sort of American nationalism, that he intends to promote.)</span></div><div><span><br></span></div><div><span>The current controversy over illegal immigration must be understood in light of these principles.<span>\u00a0 </span>In <span><a href=\"http://w2.vatican.va/content/john-paul-ii/en/messages/migration/documents/hf_jp-ii_mes_25071995_undocumented_migrants.html\">a 1996 message on World Migration Day</a></span>, John Paul II emphasized the need to welcome migrants, to take account of the dangerous circumstances they are sometimes fleeing, to avoid all racist and xenophobic attitudes, and so on.<span>\u00a0 </span>At the same time, he acknowledged that \u201cmigration is assuming the features of a social emergency, above all because of the increase in\u00a0<i>illegal migrants\u201d</i><span> (emphasis in the original), and that the problem is</span> \u201cdelicate and complex.\u201d<span>\u00a0 </span>He affirmed that \u201cillegal immigration should be prevented\u201d and that one reason it is problematic is that \u201cthe supply of foreign labour is becoming excessive in comparison to the needs of the economy, which already has difficulty in absorbing its domestic workers.\u201d<span>\u00a0 </span>And he stated that in some cases, it may be necessary to advise migrants \u201cto seek acceptance in other countries, or to return to their own country.\u201d</span></div><div><span><br></span></div><div><span>The Catechism promulgated by Pope John Paul II <span><a href=\"http://www.vatican.va/archive/ccc_css/archive/catechism/p3s2c2a4.htm\">teaches that</a></span>:</span></div><div><span><br></span></div><div><i><span>The more prosperous nations are obliged, <b>to the extent they are able,</b>to welcome the\u00a0<span>foreigner\u00a0</span>in search of the security and the means of livelihood which he cannot find in his country of origin. Public authorities should see to it that the natural right is respected that places a guest under the protection of those who receive him.</span></i></div><div><i><span><br></span></i></div><div><b><i><span>Political authorities, for the sake of the common good for which they are responsible,</span></i></b><i><span> <b>may make the exercise of the right to immigrate subject to various juridical conditions,</b> especially with regard to the immigrants' duties toward their country of adoption. <b>Immigrants are obliged to respect with gratitude the material and spiritual heritage of the country that receives them, to obey its laws and to assist in carrying civic burdens</b></span></i><b><span>.</span></b><span><span>\u00a0 </span>(Emphasis added)</span></div><div><span><br></span></div><div><span>End quote.<span>\u00a0 </span>Note that the Catechism teaches that immigrants have a <i>duty</i> to respect the laws and \u201cspiritual heritage\u201d of the nation they seek to enter, and that political authorities may restrict immigration so as to uphold the \u201ccommon good\u201d of the nation they govern.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Hence, there is no foundation in Catholic teaching for an open borders position, or for the position that those who seek to uphold the common culture and economic interests of their nation ought to be dismissed as racists and xenophobes.<span>\u00a0 </span>On the contrary, Catholic teaching <i>explicitly rules out</i> those positions.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>There is a further implication of John Paul II\u2019s teaching.<span>\u00a0 </span>It isn\u2019t merely that having a special love for one\u2019s nation and its culture is natural and virtuous.<span>\u00a0 </span>It is that a <i>failure </i>to have it is <i>vicious</i> \u2013 a violation of the fourth commandment.</span></div><div><span><br></span></div><div><span>Of course, every nation has its faults, and aspects of its history of which one ought to be ashamed.<span>\u00a0 </span>For example, Germans are right to repudiate the Nazi period of their history, and Americans are right to repudiate slavery and segregation.<span>\u00a0 </span>But there is a mentality prevalent in the modern West that goes well beyond that \u2013 that insists on seeing nothing but evil in one\u2019s own nation and its culture and history.<span>\u00a0 </span>This is the mentality <span><a href=\"https://quillette.com/2019/10/07/oikophobia-our-western-self-hatred/\">sometimes called <i>oikophobia</i></a></span> \u2013 the hatred of one\u2019s own \u201chousehold\u201d (<i>oikos</i>), in the sense of one\u2019s own nation.<span>\u00a0 </span>One sees this mentality in Westerners who shrilly and constantly denounce their civilization as irredeemably racist, colonialist, etc., downplaying or denying its virtues, and comparing it unfavorably to other cultures \u2013 as if Western culture is somehow <i>more</i> prone to such failings than other cultures are, and as if it hasn\u2019t contributed enormously to the good of the world (both of which are absurd suppositions).</span></div><div><span><br></span></div><div><span>Oikophobia is <i>evil</i>.<span>\u00a0 </span>It is a spiritual poison that damages both those prone to it (insofar as it makes them bitter, ungrateful, etc.) and the social order of which they are parts (insofar as it undermines the love and loyalty citizens need to have for their nation if it is to survive).<span>\u00a0 </span>It is analogous to the evil of hating and undermining one\u2019s own family.<span>\u00a0 </span>It is a violation of the fourth commandment.</span></div><div><span><br></span></div><div><span>The oikophobe sees his position as a remedy for nationalism, but in fact he is simply guilty of falling into an error that is the opposite extreme from that of the nationalist.<span>\u00a0 </span>Moreover, he is inadvertently <i>promoting</i> nationalism, because human beings have a tendency to overreact to one extreme by going too far in the other direction.<span>\u00a0 </span>Nationalism is bound to arise precisely as an overreaction against oikophobia.<span>\u00a0 </span>Those who are currently reacting to what they perceive as a resurgent nationalism by doubling down on oikophobia \u2013 pushing for open borders, indiscriminately denouncing their opponents as racists and xenophobes, etc. \u2013 are making a true nationalist backlash <i>more </i>likely, not less likely.<span>\u00a0 </span>The only true remedy for the evils of nationalism and oikophobia is, as John Paul II taught, the sober middle ground of patriotism.</span></div><div><span><br></span></div><div><span>It is no accident that those prone to oikophobia tend to be precisely the same people as those who want to push further the sexual revolution, feminism, and the destruction of the traditional family and traditional sex roles that these entail.<span>\u00a0 </span>The same liberal individualist poison is at the core of all of these attitudes.<span>\u00a0 </span>As St. John Paul II said, \u201c<i>patria </i>is associated with the idea and the reality of \u2018father\u2019 (<i>pater</i>).\u201d<span>\u00a0 </span>Hatred of masculinity and of the paternal authority and responsibilities that are its fulfilment, hatred of the traditional family and of the sexual morality that safeguards it, and hatred of one\u2019s fatherland, are ultimately of a piece.<span>\u00a0 </span>And lurking beneath them all is a deeper hatred for another, heavenly Father.</span></div><div><span><br></span></div><div><span>Further reading:</span></div><div><span><br></span></div><div><span><span><a href=\"https://edwardfeser.blogspot.com/2017/10/liberty-equality-fraternity.html\">Liberty, equality, fraternity?</a></span></span><span></span></div><div><span><br></span></div><div><span><span><a href=\"http://edwardfeser.blogspot.com/2019/06/continetti-on-post-liberal-conservatism_2.html\">Continetti on post-liberal conservatism</a></span></span><span></span></div><div><span><br></span></div><div><span><span><a href=\"https://www.claremont.org/crb/article/hayeks-tragic-capitalism/\">Hayek\u2019s Tragic Capitalism</a></span></span></div>"
},
"visual": {
"url": "none"
},
"unread": true,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/fbdcd69b-7e27-4b6a-bfed-6584b944155d",
"label": "\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499824479
},
{
"originId": "https://www.raywenderlich.com/5436806-modern-collection-views-with-compositional-layouts",
"fingerprint": "16268498",
"id": "RFlzskW4NhJjlZfijOSI8IXqM9+zz6V9qnDVl1gxaJs=_16e1cddfaa1:1c015:d4506071",
"updated": 1572440368000,
"summary": {
"direction": "ltr",
"content": "In this tutorial, you\u2019ll learn how to build beautiful, modern UICollectionView layouts using iOS 13\u2019s new declarative UICollectionViewCompositionalLayout API."
},
"alternate": [
{
"href": "https://www.raywenderlich.com/5436806-modern-collection-views-with-compositional-layouts",
"type": "text/html"
}
],
"crawled": 1572442340001,
"title": "Modern Collection Views with Compositional Layouts [FREE]",
"published": 1572440368000,
"origin": {
"streamId": "feed/http://www.raywenderlich.com/feed",
"htmlUrl": "http://www.raywenderlich.com/feed",
"title": "Ray Wenderlich | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more"
},
"unread": false,
"readTime": 3593,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/885f2e01-d314-4e63-abac-17dcb063f5b5",
"label": "Programming"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499811331
},
{
"originId": "https://inessential.com/2019/10/30/etas_follow_up",
"fingerprint": "d48dc2d2",
"id": "+jHfsXnBCVfCstSIW1WDumAyigT4rnsUPnI5WFxgnAU=_16e1e5e8e9e:1ca95:d4506071",
"summary": {
"direction": "ltr",
"content": "<p>Some people took my post <a href=\"https://inessential.com/2019/10/28/no_etas\">No ETAs</a> as if I were arguing against doing software estimates of any kind, ever.</p>\n<p>I didn\u2019t actually mean that. If your boss, project manager, or person you\u2019re contracting with asks for an estimate, do your best to come up with something accurate. If you\u2019re writing enterprise software, you may even be contractually bound to provide estimates for when features will ship.</p>\n<p>There are ways to get pretty good at this. Pay attention to history and avoid wishful thinking. Don\u2019t assume perfect productivity. Allow for the unexpected, because there\u2019s always something.</p>\n<p>What I\u2019m talking about is the case where you\u2019re writing a consumer-facing app \u2014\u00a0something that would get published on an app store, for instance \u2014\u00a0and customers or potential customers ask about an ETA for a given feature. Don\u2019t do it! (For the reasons stated in the article.)</p>"
},
"alternate": [
{
"href": "https://inessential.com/2019/10/30/etas_follow_up",
"type": "text/html"
}
],
"crawled": 1572467543710,
"title": "ETAs: Follow-Up",
"published": 1572466202000,
"origin": {
"streamId": "feed/http://ranchero.com/xml/rss.xml",
"htmlUrl": "https://inessential.com/",
"title": "inessential.com"
},
"unread": false,
"readTime": 6552,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499788300
},
{
"keywords": [
"Apple"
],
"originId": "https://9to5mac.com/?p=617593",
"fingerprint": "a1dafaae",
"id": "BmoAzSEWHFzR01wyxBZAhNEo11Vy8oDR1qKDe+tKVEQ=_16e1de23321:1c6e3:d4506071",
"author": "Guilherme Rambo",
"summary": {
"direction": "ltr",
"content": "<div><img src=\"https://9to5mac.com/wp-content/uploads/sites/6/2019/10/16-inch-macbook-pro-touch-bar-keyboard.jpg?quality=82&strip=all&w=1600\"></div>\n<p>Rumors about a new 16-inch MacBook Pro are not exactly new, with recent icon evidence found in macOS Catalina betas suggesting the redesign mentioned in some reports is not going to happen as we thought.</p>\n<p> <a href=\"https://9to5mac.com/2019/10/30/exclusive-16-inch-macbook-pro-touch-bar-and-touch-id-layout-confirmed/#more-617593\">more\u2026</a></p>\n<p>The post <a rel=\"nofollow\" href=\"https://9to5mac.com/2019/10/30/exclusive-16-inch-macbook-pro-touch-bar-and-touch-id-layout-confirmed/\">Exclusive: 16-inch MacBook Pro Touch Bar and Touch ID layout confirmed</a> appeared first on <a rel=\"nofollow\" href=\"https://9to5mac.com\">9to5Mac</a>.</p>"
},
"alternate": [
{
"href": "https://9to5mac.com/2019/10/30/exclusive-16-inch-macbook-pro-touch-bar-and-touch-id-layout-confirmed/",
"type": "text/html"
}
],
"crawled": 1572459393825,
"title": "Exclusive: 16-inch MacBook Pro Touch Bar and Touch ID layout confirmed",
"published": 1572456327000,
"origin": {
"streamId": "feed/http://9to5mac.com/feed/",
"htmlUrl": "https://9to5mac.com",
"title": "9to5Mac"
},
"unread": false,
"readTime": 3947,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499186128
},
{
"originId": "https://inessential.com/2019/10/30/you_choose_follow_up",
"fingerprint": "f1dd63ab",
"id": "+jHfsXnBCVfCstSIW1WDumAyigT4rnsUPnI5WFxgnAU=_16e1f71bf1c:1d086:d4506071",
"summary": {
"direction": "ltr",
"content": "<p>It came to my attention after writing my blog post about how <a href=\"https://inessential.com/2019/10/29/you_choose\">we choose the web we want</a>\u00a0that the pessimism is about not being able to make a living from blogging.</p>\n<p>Here\u2019s my followup: I don\u2019t care. Bite me.</p>"
},
"alternate": [
{
"href": "https://inessential.com/2019/10/30/you_choose_follow_up",
"type": "text/html"
}
],
"crawled": 1572485578524,
"title": "You Choose: Follow-Up",
"published": 1572482100000,
"origin": {
"streamId": "feed/http://ranchero.com/xml/rss.xml",
"htmlUrl": "https://inessential.com/",
"title": "inessential.com"
},
"unread": false,
"readTime": 3663,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1572499146275
},
{
"originId": "https://inessential.com/2019/10/14/netnewswire_os_compatibility_strategy",
"fingerprint": "fa6e6fff",
"id": "+jHfsXnBCVfCstSIW1WDumAyigT4rnsUPnI5WFxgnAU=_16dccdfc972:a6e5:d4506071",
"summary": {
"direction": "ltr",
"content": "<p>We have two goals with the app: 1) get as many people using RSS as possible, and 2) make the best app we can.</p>\n<p>To reach #2 \u2014\u00a0making the best app we can \u2014\u00a0we need to do a couple things. One is stay modern: use new APIs and tools that make the app better and easier to maintain. A second is to not spend time on things that don\u2019t make the app better. A third is to attract and retain contributors, who are usually more psyched to work with modern stuff than with old stuff.</p>\n<p>You can see how that\u2019s in a little bit of conflict with #1 (getting as many people as possible using RSS readers).</p>\n<h4>Here\u2019s the plan</h4>\n<p>After a major OS update, we will switch to requiring that update on our next major release \u2014\u00a0where major is defined as something like 5.0 or 5.1, but not something like 5.0.1. (In other words: the upcoming NetNewsWire 5.0.3 release will run on Mojave, while NetNewsWire 5.1 will require Catalina.)</p>\n<p>At the same time, we will make older versions available via the website. For instance, the last version that will run on Mojave will likely be 5.0.4 (which isn\u2019t finished yet) \u2014 and we\u2019ll make that version available indefinitely for people who haven\u2019t upgraded to Catalina.</p>\n<p>This will mean that people running older OSes will still get a high-quality app \u2014 it\u2019s just that it won\u2019t have the latest features.</p>\n<p>The key is that this allows us to make NetNewsWire the best app it can be, and making the best app we can is also part of furthering the goal of getting as many people as possible using RSS. (The biggest part, in fact. Bigger than compatibility with older OSes.)</p>\n<p>While I know this will disappoint some people, I hope you\u2019ll understand <em>why</em> we decided to do it this way. Decisions like this are never easy \u2014 there are always conflicting values to weigh, pros and cons and add up \u2014\u00a0and we don\u2019t make them impulsively. But making NetNewsWire the best app it can be has to be job #1.</p>"
},
"alternate": [
{
"href": "https://inessential.com/2019/10/14/netnewswire_os_compatibility_strategy",
"type": "text/html"
}
],
"crawled": 1571100281202,
"title": "NetNewsWire OS Compatibility Strategy",
"published": 1571099404000,
"origin": {
"streamId": "feed/http://ranchero.com/xml/rss.xml",
"htmlUrl": "https://inessential.com/",
"title": "inessential.com"
},
"unread": false,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/66132046-6f14-488d-b590-8e93422723c8",
"label": "THree"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1571123677415
},
{
"keywords": [
"Xcode"
],
"originId": "https://nshipster.com/swiftui-previews",
"recrawled": 1571407228188,
"updateCount": 2,
"fingerprint": "7582ddff",
"id": "08l+9ftpGejQ9f/2DZ6dom5rSnNJJO9OCox6I3nUnWg=_16dc8d7749a:96ed:d4506071",
"updated": 1571036400000,
"author": "Mattt",
"summary": {
"direction": "ltr",
"content": "<p>Working on a large iOS codebase often involves a lot of waiting. But with Xcode 11, our wait is finally over \u2014 and it\u2019s all thanks to SwiftUI.</p>"
},
"alternate": [
{
"href": "https://nshipster.com/swiftui-previews/",
"type": "text/html"
}
],
"crawled": 1571032626330,
"title": "SwiftUI Previews on macOS Catalina and Xcode 11",
"published": 1571036400000,
"origin": {
"streamId": "feed/http://nshipster.com/feed.xml",
"htmlUrl": "https://nshipster.com/",
"title": "NSHipster"
},
"content": {
"direction": "ltr",
"content": "<p>Working on a large iOS codebase often involves a lot of waiting:\nWaiting for Xcode to index your files,\nwaiting for Swift and Objective-C code to compile,\nwaiting for the Simulator to boot and your app to launch\u2026</p>\n<p>And after all of that,\nyou spend even more time getting your app\ninto a particular state and onto a particular screen,\njust to see whether the Auto Layout constraint you just added\nfixes that regression you found.\nIt didn\u2019t, of course,\nso you jump back into Xcode,\ntweak the Content Hugging Priority,\nhit <kbd>\u2318</kbd><kbd>R</kbd>,\nand start the whole process again.</p>\n<p>We might relate our sorry predicament to\n<a rel=\"noopener noreferrer\" href=\"https://xkcd.com/303/\">that one xkcd comic</a>,\nbut for those of us who don\u2019t so much relish in\nthe stop-and-go nature of app development,\nthere\u2019s an old Yiddish joke about Shlemiel the painter\n<em>(provided below with a few \uf8ff-specific modifications;\nfor the uninitiated,\nplease refer to Joel Spolsky\u2019s\n<a rel=\"noopener noreferrer\" href=\"https://www.joelonsoftware.com/2001/12/11/back-to-basics/\">original telling</a>)</em>:</p>\n<blockquote>\n<p>Shlemiel gets a job as a software developer,\nimplementing a new iOS app.\nOn the first sprint he opens Xcode\nand implements 10 new screens of the app.\n<em>\u201cThat\u2019s pretty good!\u201d</em> says his manager,\n<em>\u201cyou\u2019re a fast worker!\u201d</em> and pays him a Bitcoin.</p>\n<p>The next sprint Shlemiel only gets 5 screens done.\n<em>\u201cWell, that\u2019s not nearly as good as yesterday,\nbut you\u2019re still a fast worker. 5 screens is respectable,\u201d</em>\nand pays him a Bitcoin.</p>\n<p>The next sprint Shlemiel implements 1 screen.\n<em>\u201cOnly 1!\u201d</em> shouts his manager.\n<em>\u201cThat\u2019s unacceptable!\nOn the first day you did ten times that much work!\nWhat\u2019s going on?\u201d</em></p>\n<p><em>\u201cI can\u2019t help it,\u201d</em> says Shlemiel.\n<em>\u201cEach sprint I get further and further away from\n<code>application(_:did<wbr>Finish<wbr>Launching<wbr>With<wbr>Options:)</code>!\u201d</em></p>\n</blockquote>\n<p>Over the years,\nthere have been some developments that\u2019ve helped things slightly,\nincluding\n<a href=\"https://nshipster.com/ibinspectable-ibdesignable/\"><code>@IBInspectable</code> and <code>@IBDesignable</code></a>\nand <a href=\"https://nshipster.com/xcplayground/\">Xcode Playgrounds</a>.\nBut with Xcode 11,\nour wait is finally over \u2014\nand it\u2019s all thanks to SwiftUI.</p>\n<hr>\n<aside>\n<p>The functionality described in this article requires the following:</p>\n<ul>\n<li><strong>Xcode 11</strong></li>\n<li><strong>macOS Catalina</strong></li>\n<li>\n<strong>iOS 13</strong> set as the <strong>Deployment Target</strong> for your app\u2019s <strong>Debug</strong> configuration <br>\n<em>(In Xcode, navigate your project\u2019s Build Settings;\nunder the Deployment heading,\nexpand the iOS Deployment Target setting and set Debug to iOS 13.0 or later)</em>\n</li>\n</ul>\n<p>Without these three things,\nyour code either won\u2019t compile or won\u2019t render live previews.</p>\n</aside>\n<hr>\n<p>Although many of us have taken a <a href=\"https://nshipster.com/wwdc-2019/\"><em>\u201cwait and see\u201d</em> approach</a> to SwiftUI,\nwe can start using its capabilities <strong>today</strong>\nto radically speed up and improve our development process \u2014\n<em>without changing a line of code in our UIKit apps</em>.</p>\n<p>Consider a subclass of <code>UIButton</code>\nthat draws a border around itself:</p>\n<pre data-lang=\"Swift\"><code><span>final</span> <span>class</span> <span>Bordered<wbr>Button</span><span>:</span> <span>UIButton</span> <span>{</span>\n <span>var</span> <span>corner<wbr>Radius</span><span>:</span> <span>CGFloat</span> <span>{</span> <var>...</var> <span>}</span>\n <span>var</span> <span>border<wbr>Width</span><span>:</span> <span>CGFloat</span> <span>{</span> <var>...</var> <span>}</span>\n <span>var</span> <span>border<wbr>Color</span><span>:</span> <span>UIColor</span><span>?</span> <span>{</span> <var>...</var> <span>}</span>\n <span>}</span>\n </code></pre>\n<p>Normally,\nif we wanted to test how our UI element performs,\nwe\u2019d have to add it to a view in our app,\nbuild and run,\nand navigate to that screen.\nBut with Xcode 11,\nwe can now see a preview side-by-side with the code editor\nby adding the following under the original declaration of <code>Bordered<wbr>Button</code>:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>#if can<wbr>Import(Swift<wbr>UI) &amp;&amp; DEBUG</span>\n <span>import</span> <span>Swift<wbr>UI</span>\n <span>@available(i<wbr>OS 13.0, *)</span>\n <span>struct</span> <span>Bordered<wbr>Button_Preview</span><span>:</span> <span>Preview<wbr>Provider</span> <span>{</span>\n <span>static</span> <span>var</span> <span>previews</span><span>:</span> <span>some</span> <span>View</span> <span>{</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Bordered<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title</span><span>(</span><span>&quot;Follow&quot;</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>tint<wbr>Color</span> <span>=</span> <span>.</span><span>system<wbr>Orange</span>\n <span>button</span><span>.</span><span>set<wbr>Title<wbr>Color</span><span>(</span><span>.</span><span>system<wbr>Orange</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>return</span> <span>button</span>\n <span>}</span><span>.</span><span>preview<wbr>Layout</span><span>(</span><span>.</span><span>size<wbr>That<wbr>Fits</span><span>)</span>\n <span>.</span><span>padding</span><span>(</span><span>10</span><span>)</span>\n <span>}</span>\n <span>}</span>\n <span>#endif</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI preview with Follow button\" src=\"https://nshipster.com/assets/swiftui-preview-follow-94a523cd048a0cd14d78414d55d238e9c57abd3ed7ea30dd9a6fb73facc05d78.png\"></p>\n</aside>\n</div>\n<p>Using a new feature called <dfn>dynamic replacement</dfn>,\nXcode can update this preview without recompiling \u2014\nwithin moments of your making a code change.\nThis lets you rapidly prototype changes like never before.</p>\n<p>Want to see how your button handles long titles?\nBang away on your keyboard within the call to <code>set<wbr>Title(_:for:)</code>\nin your preview,\nand test out potential fixes in your underlying implementation\nwithout so much as leaving your current file!</p>\n<aside>\n<p><code>UIView<wbr>Preview</code> is a custom, generic structure\nthat we created to conveniently host previews of <code>UIView</code> subclasses.\nFeel free to <a rel=\"noopener noreferrer\" href=\"https://gist.github.com/mattt/ff6b58af8576c798485b449269d43607\">download the source</a>\nand add it to your project directly.</p>\n<p>Incorporating a proper dependency would be complicated by\nthe conditional import and iOS 13 Deployment Target settings\nrequired to make Xcode Previews work for non-SwiftUI apps,\nso in this particular instance,\nwe think it\u2019s best to embed these files directly.</p>\n<details>\n\n<pre data-lang=\"Swift\"><code><span>import</span> <span>UIKit</span>\n <span>#if can<wbr>Import(Swift<wbr>UI) &amp;&amp; DEBUG</span>\n <span>import</span> <span>Swift<wbr>UI</span>\n <span>struct</span> <span>UIView<wbr>Preview</span><span>&lt;</span><span>View</span><span>:</span> <span>UIView</span><span>></span><span>:</span> <span>UIView<wbr>Representable</span> <span>{</span>\n <span>let</span> <span>view</span><span>:</span> <span>View</span>\n <span>init</span><span>(</span><span>_</span> <span>builder</span><span>:</span> <span>@escaping</span> <span>()</span> <span>-></span> <span>View</span><span>)</span> <span>{</span>\n <span>view</span> <span>=</span> <span>builder</span><span>()</span>\n <span>}</span>\n <span>// MARK: - UIView<wbr>Representable</span>\n <span>func</span> <span>make<wbr>UIView</span><span>(</span><span>context</span><span>:</span> <span>Context</span><span>)</span> <span>-></span> <span>UIView</span> <span>{</span>\n <span>return</span> <span>view</span>\n <span>}</span>\n <span>func</span> <span>update<wbr>UIView</span><span>(</span><span>_</span> <span>view</span><span>:</span> <span>UIView</span><span>,</span> <span>context</span><span>:</span> <span>Context</span><span>)</span> <span>{</span>\n <span>view</span><span>.</span><span>set<wbr>Content<wbr>Hugging<wbr>Priority</span><span>(</span><span>.</span><span>default<wbr>High</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>horizontal</span><span>)</span>\n <span>view</span><span>.</span><span>set<wbr>Content<wbr>Hugging<wbr>Priority</span><span>(</span><span>.</span><span>default<wbr>High</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>vertical</span><span>)</span>\n <span>}</span>\n <span>}</span>\n <span>#endif</span>\n </code></pre>\n</details>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/swiftui-previews/#previewing-multiple-states\"></a>Previewing Multiple States</h2>\n<p>Let\u2019s say our app had a <code>Favorite<wbr>Button</code> \u2014\na distant cousin (perhaps by composition) to <code>Bordered<wbr>Button</code>.\nIn its default state,\nit shows has the title \u201cFavorite\u201d\nand displays a <span title=\"Heart\">\u2661</span> icon.\nWhen its <code>is<wbr>Favorited</code> property is set to <code>true</code>,\nthe title is set to \u201cUnfavorite\u201d\nand displays a <span title=\"Heart with slash\">\u2661\u0338</span> icon.</p>\n<p>We can preview both at once\nby wrapping two <code>UIView<wbr>Preview</code> instances within a single SwiftUI <code>Group</code>:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>Group</span> <span>{</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Favorite<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>return</span> <span>button</span>\n <span>}</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Favorite<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>button</span><span>.</span><span>is<wbr>Favorited</span> <span>=</span> <span>true</span>\n <span>return</span> <span>button</span>\n <span>}</span>\n <span>}</span><span>.</span><span>preview<wbr>Layout</span><span>(</span><span>.</span><span>size<wbr>That<wbr>Fits</span><span>)</span>\n <span>.</span><span>padding</span><span>(</span><span>10</span><span>)</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI previews with Favorite and Unfavorite buttons\" src=\"https://nshipster.com/assets/swiftui-preview-favorite-unfavorite-e0658904f16daa64c98d7ae4e9e35d011f3c1996da29b3aecc4a8c438298e13f.png\"></p>\n</aside>\n</div>\n<aside>\n<p>The chained <code>preview<wbr>Layout</code> and <code>padding</code> methods\napply to each member of the <code>Group</code>.\nYou can use these and\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/documentation/swiftui/view\">other <code>View</code> methods</a>\nto change the appearance of your previews.</p>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/swiftui-previews/#previewing-dark-mode\"></a>Previewing Dark Mode</h2>\n<p>With <a href=\"https://nshipster.com/dark-mode/\">Dark Mode in iOS 13</a>,\nit\u2019s always a good idea to double-check that your custom views\nare configured with dynamic colors\nor accommodate both light and dark appearance in some other way.</p>\n<p>An easy way to do this\nwould be to use a <code>For<wbr>Each</code> element\nto render a preview for each case in the <code>Color<wbr>Scheme</code> enumeration:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>For<wbr>Each</span><span>(</span><span>Color<wbr>Scheme</span><span>.</span><span>all<wbr>Cases</span><span>,</span> <span>id</span><span>:</span> <span>\\</span><span>.</span><span>self</span><span>)</span> <span>{</span> <span>color<wbr>Scheme</span> <span>in</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Bordered<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title</span><span>(</span><span>&quot;Subscribe&quot;</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Image</span><span>(</span><span>UIImage</span><span>(</span><span>system<wbr>Name</span><span>:</span> <span>&quot;plus&quot;</span><span>),</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title<wbr>Color</span><span>(</span><span>.</span><span>system<wbr>Orange</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>tint<wbr>Color</span> <span>=</span> <span>.</span><span>system<wbr>Orange</span>\n <span>return</span> <span>button</span>\n <span>}</span><span>.</span><span>environment</span><span>(\\</span><span>.</span><span>color<wbr>Scheme</span><span>,</span> <span>color<wbr>Scheme</span><span>)</span>\n <span>.</span><span>preview<wbr>Display<wbr>Name</span><span>(</span><span>&quot;</span><span>\\(</span><span>color<wbr>Scheme</span><span>)</span><span>&quot;</span><span>)</span>\n <span>}</span><span>.</span><span>preview<wbr>Layout</span><span>(</span><span>.</span><span>size<wbr>That<wbr>Fits</span><span>)</span>\n <span>.</span><span>background</span><span>(</span><span>Color</span><span>(</span><span>.</span><span>system<wbr>Background</span><span>))</span>\n <span>.</span><span>padding</span><span>(</span><span>10</span><span>)</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI previews with different color schemes\" src=\"https://nshipster.com/assets/swiftui-preview-color-schemes-34bd0a96b6e3768b29f0ddd537ee99243fed2430bc42a7a4f535fad5ec533fbe.png\"></p>\n</aside>\n</div>\n<aside>\n<p>When rendering previews with <code>For<wbr>Each</code>,\nuse the <code>preview<wbr>Display<wbr>Name</code> method to help distinguish among\nall of the enumerated values.</p>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/swiftui-previews/#previewing-dynamic-type-size-categories\"></a>Previewing Dynamic Type Size Categories</h2>\n<p>We can use the same approach to preview our views in various\n<a rel=\"noopener noreferrer\" href=\"https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/\">Dynamic Type Sizes</a>:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>For<wbr>Each</span><span>(</span><span>Content<wbr>Size<wbr>Category</span><span>.</span><span>all<wbr>Cases</span><span>,</span> <span>id</span><span>:</span> <span>\\</span><span>.</span><span>self</span><span>)</span> <span>{</span> <span>size<wbr>Category</span> <span>in</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Bordered<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title</span><span>(</span><span>&quot;Subscribe&quot;</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Image</span><span>(</span><span>UIImage</span><span>(</span><span>system<wbr>Name</span><span>:</span> <span>&quot;plus&quot;</span><span>),</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title<wbr>Color</span><span>(</span><span>.</span><span>system<wbr>Orange</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>tint<wbr>Color</span> <span>=</span> <span>.</span><span>system<wbr>Orange</span>\n <span>return</span> <span>button</span>\n <span>}</span><span>.</span><span>environment</span><span>(\\</span><span>.</span><span>size<wbr>Category</span><span>,</span> <span>size<wbr>Category</span><span>)</span>\n <span>.</span><span>preview<wbr>Display<wbr>Name</span><span>(</span><span>&quot;</span><span>\\(</span><span>size<wbr>Category</span><span>)</span><span>&quot;</span><span>)</span>\n <span>}</span><span>.</span><span>preview<wbr>Layout</span><span>(</span><span>.</span><span>size<wbr>That<wbr>Fits</span><span>)</span>\n <span>.</span><span>padding</span><span>(</span><span>10</span><span>)</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI previews with different content size categories\" src=\"https://nshipster.com/assets/swiftui-preview-content-size-categories-33be9504b46e4fbfc96f152a165378b9b517c1ca54c20ea21096a7767c49dd67.png\"></p>\n</aside>\n</div>\n<h2>\n<a href=\"https://nshipster.com/swiftui-previews/#previewing-different-locales\"></a>Previewing Different Locales</h2>\n<p>Xcode Previews are especially time-saving when it comes to\nlocalizing an app into multiple languages.\nCompared to the hassle of configuring Simulator\nback and forth between different languages and regions,\nthis new approach makes a world of difference.</p>\n<p>Let\u2019s say that, in addition to English,\nyour app supported various <a rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/Right-to-left\">right-to-left languages</a>.\nYou could verify that your\n<abbr title=\"Right-to-Left\">RTL</abbr> logic worked as expected like so:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>let</span> <span>supported<wbr>Locales</span><span>:</span> <span>[</span><span>Locale</span><span>]</span> <span>=</span> <span>[</span>\n <span>&quot;en-US&quot;</span><span>,</span> <span>// English (United States)</span>\n <span>&quot;ar-QA&quot;</span><span>,</span> <span>// Arabic (Qatar)</span>\n <span>&quot;he-IL&quot;</span><span>,</span> <span>// Hebrew (Israel)</span>\n <span>&quot;ur-IN&quot;</span> <span>// Urdu (India)</span>\n <span>]</span><span>.</span><span>map</span><span>(</span><span>Locale</span><span>.</span><span>init</span><span>(</span><span>identifier</span><span>:))</span>\n <span>func</span> <span>localized<wbr>String</span><span>(</span><span>_</span> <span>key</span><span>:</span> <span>String</span><span>,</span> <span>for</span> <span>locale</span><span>:</span> <span>Locale</span><span>)</span> <span>-></span> <span>String</span><span>?</span> <span>{</span> <var>...</var> <span>}</span>\n <span>return</span> <span>For<wbr>Each</span><span>(</span><span>supported<wbr>Locales</span><span>,</span> <span>id</span><span>:</span> <span>\\</span><span>.</span><span>identifier</span><span>)</span> <span>{</span> <span>locale</span> <span>in</span>\n <span>UIView<wbr>Preview</span> <span>{</span>\n <span>let</span> <span>button</span> <span>=</span> <span>Bordered<wbr>Button</span><span>(</span><span>frame</span><span>:</span> <span>.</span><span>zero</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title</span><span>(</span><span>localized<wbr>String</span><span>(</span><span>&quot;Subscribe&quot;</span><span>,</span> <span>for</span><span>:</span> <span>locale</span><span>),</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Image</span><span>(</span><span>UIImage</span><span>(</span><span>system<wbr>Name</span><span>:</span> <span>&quot;plus&quot;</span><span>),</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>set<wbr>Title<wbr>Color</span><span>(</span><span>.</span><span>system<wbr>Orange</span><span>,</span> <span>for</span><span>:</span> <span>.</span><span>normal</span><span>)</span>\n <span>button</span><span>.</span><span>tint<wbr>Color</span> <span>=</span> <span>.</span><span>system<wbr>Orange</span>\n <span>return</span> <span>button</span>\n <span>}</span><span>.</span><span>environment</span><span>(\\</span><span>.</span><span>locale</span><span>,</span> <span>locale</span><span>)</span>\n <span>.</span><span>preview<wbr>Display<wbr>Name</span><span>(</span><span>Locale</span><span>.</span><span>current</span><span>.</span><span>localized<wbr>String</span><span>(</span><span>for<wbr>Identifier</span><span>:</span> <span>locale</span><span>.</span><span>identifier</span><span>))</span>\n <span>}</span><span>.</span><span>preview<wbr>Layout</span><span>(</span><span>.</span><span>size<wbr>That<wbr>Fits</span><span>)</span>\n <span>.</span><span>padding</span><span>(</span><span>10</span><span>)</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI previews with different locales\" src=\"https://nshipster.com/assets/swiftui-preview-locales-4a05412203ff6074a58a86ad4e5e742e8250d2f70ae898c27c19fa5bd4a405f5.png\"></p>\n</aside>\n</div>\n<aside>\n<p>We don\u2019t know of an easy way to use <code>NSLocalized<wbr>String</code> with an explicit locale.\nYou could go to the trouble of retrieving localized strings\nfrom a strings file in your bundle,\nbut in most cases,\nyou\u2019ll be just fine hard-coding text in your previews.</p>\n</aside>\n<h2>\n<a href=\"https://nshipster.com/swiftui-previews/#previewing-view-controllers-on-different-devices\"></a>Previewing View Controllers on Different Devices</h2>\n<p>SwiftUI previews aren\u2019t limited to views,\nyou can also use them with view controllers.\nBy creating a <a rel=\"noopener noreferrer\" href=\"https://gist.github.com/mattt/ff6b58af8576c798485b449269d43607\">custom <code>UIView<wbr>Controller<wbr>Preview</code> type</a>\nand taking advantage of some\n<a href=\"https://nshipster.com/ios-13/#remove-implicitly-unwrapped-optionals-from-view-controllers-initialized-from-storyboards\">new <code>UIStoryboard</code> class methods in iOS 13</a>,\nwe can easily preview our view controller\non various devices \u2014\none on top of another:</p>\n<div>\n<pre data-lang=\"Swift\"><code><span>#if can<wbr>Import(Swift<wbr>UI) &amp;&amp; DEBUG</span>\n <span>import</span> <span>Swift<wbr>UI</span>\n <span>let</span> <span>device<wbr>Names</span><span>:</span> <span>[</span><span>String</span><span>]</span> <span>=</span> <span>[</span>\n <span>&quot;i<wbr>Phone SE&quot;</span><span>,</span>\n <span>&quot;i<wbr>Pad 11 Pro Max&quot;</span><span>,</span>\n <span>&quot;i<wbr>Pad Pro (11-inch)&quot;</span>\n <span>]</span>\n <span>@available(i<wbr>OS 13.0, *)</span>\n <span>struct</span> <span>View<wbr>Controller_Preview</span><span>:</span> <span>Preview<wbr>Provider</span> <span>{</span>\n <span>static</span> <span>var</span> <span>previews</span><span>:</span> <span>some</span> <span>View</span> <span>{</span>\n <span>For<wbr>Each</span><span>(</span><span>device<wbr>Names</span><span>,</span> <span>id</span><span>:</span> <span>\\</span><span>.</span><span>self</span><span>)</span> <span>{</span> <span>device<wbr>Name</span> <span>in</span>\n <span>UIView<wbr>Controller<wbr>Preview</span> <span>{</span>\n <span>UIStoryboard</span><span>(</span><span>name</span><span>:</span> <span>&quot;Main&quot;</span><span>,</span> <span>bundle</span><span>:</span> <span>nil</span><span>)</span>\n <span>.</span><span>instantiate<wbr>Initial<wbr>View<wbr>Controller</span> <span>{</span> <span>coder</span> <span>in</span>\n <span>View<wbr>Controller</span><span>(</span><span>coder</span><span>:</span> <span>coder</span><span>)</span>\n <span>}</span><span>!</span>\n <span>}</span><span>.</span><span>preview<wbr>Device</span><span>(</span><span>Preview<wbr>Device</span><span>(</span><span>raw<wbr>Value</span><span>:</span> <span>device<wbr>Name</span><span>))</span>\n <span>.</span><span>preview<wbr>Display<wbr>Name</span><span>(</span><span>device<wbr>Name</span><span>)</span>\n <span>}</span>\n <span>}</span>\n <span>}</span>\n <span>#endif</span>\n </code></pre>\n<aside>\n<p><img alt=\"SwiftUI previews with different devices\" src=\"https://nshipster.com/assets/swiftui-preview-devices-c15a3ade082a4860c53f81fc6b5f3975e92d1570663c9e79cd10889d872564ad.png\"></p>\n</aside>\n</div>\n<aside>\n<p>There\u2019s currently no way to get SwiftUI device previews in landscape orientation.\nAlthough you can approximate this with a fixed size preview layout,\nbe aware that it won\u2019t respect Safe Area on iPhone\nor render split views correctly on iPad.</p>\n</aside>\n<hr>\n<p>Although most of us are still some years away from shipping SwiftUI in our apps\n(whether by choice or necessity),\nwe can all immediately benefit from the order-of-magnitude improvement\nit enables with Xcode 11 on macOS Catalina.</p>\n<p>By eliminating so much time spent waiting for things to happen,\nwe not only get (literally) <em>hours</em> more time each week,\nbut we unlock the possibility of maintaining an unbroken flow state during that time.\nNot only that,\nbut the convenience of integrated tests\nfundamentally changes the calculus for testing:\ninstead of being a rare <em>\u201cnice to have,\u201d</em>\nthey\u2019re the new default.\nPlus:\nthese inline previews serve as living documentation\nthat can help teams both large and small\nfinally get a handle on their design system.</p>\n<p>It\u2019s hard to overstate how much of a game-changer Xcode Previews are for iOS development,\nand we couldn\u2019t be happier to incorporate them into our workflow.</p>\n"
},
"visual": {
"url": "none"
},
"unread": false,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/885f2e01-d314-4e63-abac-17dcb063f5b5",
"label": "Programming"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1571037592868
},
{
"originId": "tag:blogger.com,1999:blog-8954608646904080796.post-3215871338266756283",
"fingerprint": "717870dc",
"thumbnail": [
{
"url": "https://1.bp.blogspot.com/-JTONCNpv2X8/XaEUgnGZSzI/AAAAAAAADTg/EaM5cDjkD3kRnXPIR0-6AX-3VxlC_br_QCEwYBhgL/s72-c/091.jpg",
"width": 72,
"height": 72
}
],
"id": "v0v+7Ya8tssIZvd3/pcnFRr3HwvY/5YK3FGc2t65c0Y=_16dbd619e56:82fa:d4506071",
"updated": 1570837682218,
"author": "Edward Feser",
"alternate": [
{
"href": "http://edwardfeser.blogspot.com/2019/10/around-web.html",
"type": "text/html"
}
],
"crawled": 1570840354390,
"title": "Around the web",
"published": 1570837680000,
"origin": {
"streamId": "feed/http://edwardfeser.blogspot.com/feeds/posts/default",
"htmlUrl": "http://edwardfeser.blogspot.com/",
"title": "Edward Feser"
},
"content": {
"direction": "ltr",
"content": "<br><div></div><div></div><div><a href=\"https://1.bp.blogspot.com/-JTONCNpv2X8/XaEUgnGZSzI/AAAAAAAADTg/EaM5cDjkD3kRnXPIR0-6AX-3VxlC_br_QCEwYBhgL/s1600/091.jpg\"><img border=\"0\" src=\"https://1.bp.blogspot.com/-JTONCNpv2X8/XaEUgnGZSzI/AAAAAAAADTg/EaM5cDjkD3kRnXPIR0-6AX-3VxlC_br_QCEwYBhgL/s200/091.jpg\" data-original-width=\"166\" width=\"136\" data-original-height=\"243\" height=\"200\"></a></div><div><span>At <i>The Catholic Thing</i>, Fr. Thomas Weinandy on <span><a href=\"https://www.thecatholicthing.org/2019/10/08/pope-francis-and-schism/\">the studied ambiguity of Pope Francis.</a></span>\u00a0 In his new book <i>Conciliar Octet</i>, Fr. Aidan Nichols on <a href=\"https://www.catholicworldreport.com/2019/08/22/fr-aidan-nichols-conciliar-octet-is-a-welcome-guide-to-the-eight-key-texts-of-vatican-ii/\">the hermeneutic of continuity and Vatican II</a>. </span></div><div><span><br></span></div><div><span>At <i>Medium</i>, philosopher Kathleen Stock on <span><a href=\"https://medium.com/@kathleenstock/are-academics-freely-able-to-criticise-the-idea-of-gender-identity-in-uk-universities-67b97c6e04be\">gender theory versus academic freedom in the UK</a></span>.<span>\u00a0 </span>At <i>Inside Higher Education</i>, twelve prominent philosophers <span><a href=\"https://www.insidehighered.com/views/2019/07/22/philosophers-should-not-be-sanctioned-their-positions-sex-and-gender-opinion\">defend the right to free inquiry on matters of sex and gender</a></span>.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span>Philosopher Daniel A. Kaufman on <span><a href=\"https://theelectricagora.com/2019/09/08/philosophys-aspirant-tin-pot-dictators/\">the \u201cwoke\u201d fanatics increasingly infesting academic philosophy</a></span>, at <i>The Electric Agora</i>.<span>\u00a0 </span>Richard Marshall <span><a href=\"https://316am.site123.me/articles/anti-theory-philosophy?c=end-times-series\">interviews Kaufman</a></span> at <i>3:16</i>.<span>\u00a0</span></span></div><a name=\"more\"></a> <br> <div><span>Peggy Noonan on <u><a href=\"http://peggynoonan.com/what-were-robespierres-pronouns/\">transgender Jacobinism</a></u>, at <i>The Wall Street Journal</i>.<span>\u00a0 </span>At YouTube, <u><a href=\"https://www.youtube.com/watch?v=IzNGkwGYE4E\">video of an indoctrination session</a></u>.<span><span></span></span></span></div><div><span><br></span></div><div><span>Jacob Howland on <span><a href=\"https://newcriterion.com/issues/2019/10/borgess-mirror\">Borges\u2019s Library of Babel</a></span>, at <i>The New Criterion</i>.</span></div><div><span><br></span></div><div><span>At <i>New Statesman</i>, John Gray on Tom Holland on <u><a href=\"https://www.newstatesman.com/dominion-making-western-mind-tom-holland-review\">the Christian origins of modern secular liberal values</a></u>.<span>\u00a0 </span>More reviews <u><a href=\"https://kirkcenter.org/reviews/the-origin-of-the-secular-species/\">at <i>The University Bookman</i></a></u> and <u><a href=\"https://literaryreview.co.uk/it-began-in-a-manger\">at <i>Literary Review</i></a></u>.</span></div><div><span><br></span></div><div><span>At <i>Quillette</i>, Benedict Beckeld diagnoses <span><a href=\"https://quillette.com/2019/10/07/oikophobia-our-western-self-hatred/\">Western self-hatred or <i>oikophobia</i></a></span>.</span></div><div><span><br></span></div><div><u><span><a href=\"http://www.axs.tv/featured/shaffer-105/\">Donald Fagen interviewed</a></span></u><span> on <i>Paul Shaffer Plus One</i>.</span></div><div><span><br></span></div><div><span>Kay Hymowitz on <span><a href=\"https://www.washingtonexaminer.com/opinion/a-nation-dying-in-despair-and-family-breakdown-is-part-of-the-problem\">the sexual revolution and mental health</a></span>, at <i>The Washington Examiner</i>.</span></div><div><span><br></span></div><div><span>John DeRosa of the Classical Theism Podcast <span><a href=\"http://www.classicaltheism.com/kerr/\">interviews Thomist philosopher Gaven Kerr</a></span> on the topic of Aquinas and creation.</span></div><div><span><br></span></div><div><span>Ronald W. Dworkin on <span><a href=\"https://www.the-american-interest.com/2019/10/08/artificial-intelligence-whats-to-fear/\">\u201cartificial intelligence\u201d as a projection of artificial intelligence researchers</a></span>, at <i>The American Interest</i>.</span></div><div><span><br></span></div><div><span>New books on Aquinas: <span><i><a href=\"https://global.oup.com/academic/product/aquinas-and-the-metaphysics-of-creation-9780190941307?cc=us&lang=en&\">Aquinas and the Metaphysics of Creation</a></i></span>, by Gaven Kerr; <span><i><a href=\"https://www.hfsbooks.com/books/the-discovery-of-being-and-thomas-aquinas-cullen-harkins/\">The Discovery of Being and Thomas Aquinas</a></i></span>, edited by Christopher Cullen and Franklin Harkins; <span><i><a href=\"https://www.springer.com/gp/book/9783030339111#aboutAuthors\">The Human Person: What Aristotle and Thomas Aquinas Offer Modern Psychology</a></i></span>, by Thomas Spalding, James Stedman, Christina Gagn\u00e9, and Matthew Kostelecky</span><span>.<span></span></span></div><div><span><br></span></div><div><span>At the <i>Institute of Art and Ideas</i>: Philosopher of physics Tim Maudlin on <span><a href=\"https://iai.tv/articles/quantum-theory-and-common-sense-auid-1254\">quantum physics and common sense</a></span>.<span>\u00a0 </span>Physicist Subir Sarkar and philosophers Nancy Cartwright and John Dupr\u00e9 <span><a href=\"https://iai.tv/video/the-end-of-all-things-what-is-reality-made-of-metaphysics\">discuss physics and materialism</a></span>.</span></div><div><span><br></span></div><div><span>Philosopher Dennis Bonnette on <span><a href=\"https://strangenotions.com/why-humans-are-more-than-mere-animals/\">the distinction between the intellect and the imagination</a></span>, at <i>Strange Notions</i>.</span></div><div><span><br></span></div><div><span>Philosopher of time <span><a href=\"https://316am.site123.me/articles/moving-spotlight-metaphysics-and-other-stuff?c=end-times-series\">Ross Cameron is interviewed</a></span> by Richard Marshall at <i>3:16</i>.</span></div><div><span><br></span></div><div><span>Duns Scotus in focus at <i><u><a href=\"https://philosophynow.org/issues/127/Duns_Scotus_1265_66-1308?utm_term=Autofeed&utm_medium=Social&cpg=ebtw&utm_source=Twitter#Echobox=1562194883\">Philosophy Now</a></u></i> and <i><u><a href=\"https://www.commonwealmagazine.org/have-you-tried-scotus\">Commonweal</a></u></i>.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><span><a href=\"http://mentalfloss.com/article/585008/alfred-hitchcock-presents-facts\">10 facts about\u00a0<i>Alfred Hitchcock Presents</i></a><span>, at <i>Mental Floss</i>.</span></span></div><div><br></div><div><span>Tim Maudlin on <span><a href=\"https://bostonreview.net/science-nature/tim-maudlin-why-world?fbclid=IwAR3m9AO4OVK9FkgSZ5sI8YQTw8ADc4QQ2UCn0Sm5PclvxqvjhJjnjMy_NPc\">Judea Pearl on causation versus correlation</a></span>, at the <i>Boston Review</i>.<span>\u00a0 </span>Maudlin\u2019s book <i><span>Philosophy of Physics: Quantum Theory</span></i><span> <span><a href=\"https://ndpr.nd.edu/news/philosophy-of-physics-quantum-theory/\">is reviewed</a></span> at <i>Notre Dame Philosophical Reviews</i>.</span></span></div><div><span><span><br></span></span></div><div><span>Charles Styles interviews Peter Harrison on the subject of <span><a href=\"https://fivebooks.com/best-books/history-science-and-religion-peter-harrison/\">the best books on the history of science and religion</a></span>, at <i>Five Books</i>.</span></div><div><span><br></span></div><div><span>At <i>Quillette</i>, Kevin Mims on <span><i><a href=\"https://quillette.com/2019/09/13/william-peter-blattys-counter-countercultural-parable/\">The Exorcist<span> as a film about the breakdown of the family</span></a></i></span>.</span></div><div><span><br></span></div><div><i><span>Society in Mind</span></i><span> on <span><a href=\"https://societyinmind.com/2019/08/13/beneath-the-replication-crisis/\">the replication crisis in psychology</a></span>.</span></div><div><span><br></span></div><div><span>Matias Slavov on <span><a href=\"https://aeon.co/essays/what-albert-einstein-owes-to-david-humes-notion-of-time\">Hume and Einstein on the nature of time</a></span>, at <i>Aeon</i>.</span></div><div><span><br></span></div><div><span>At <i>Catholic World Report</i>, philosopher Joseph Trabbic on <span><a href=\"https://www.catholicworldreport.com/2019/09/18/thomism-and-political-liberalism-part-2/\">Aquinas and political liberalism</a></span>.<span><span></span></span></span></div><div><span><br></span></div><div><i><span>Boston Review</span></i><span> on <span><a href=\"https://bostonreview.net/philosophy-religion/katrina-forrester-future-political-philosophy\">post-liberal academic political philosophy</a></span>.<span>\u00a0 </span><i>The Chronicle of Higher Education</i>on <span><a href=\"https://www.chronicle.com/interactives/20190912-academias-holy-warriors?key=mi0Bff1vaLHL09_no2EmgyubkYGaE_Z0wYeVvMXRpBMlzD16oZl7OvOuo9-6VB1CQ1pUYTE5WnhOR1J5VUFMaG1lRk1TdExEQ1VIbkRnQXRiYkZkdzh4VER0MA\">post-liberal Catholic political philosophy</a></span>.<span>\u00a0 </span></span></div><div><span><span><br></span></span></div><div><i><span><a href=\"https://www.amazon.com/Blue-World-John-Coltrane/dp/B07VQTJ24X/ref=tmm_acd_swatch_0?_encoding=UTF8&qid=1570834820&sr=8-1\">Blue World</a></span></i><span>, an album of lost John Coltrane tracks, <a href=\"https://www.theguardian.com/music/2019/sep/27/john-coltrane-blue-world-review\">has been released</a>.</span></div><div><span><br></span></div><div><span>It\u2019s a thing.<span>\u00a0 </span><i>The Huffington Post</i> reports on <span><a href=\"https://www.huffpost.com/highline/article/millennial-nuns/?guce_referrer=aHR0cHM6Ly90LmNvL1JIdXVhTjlTMHU&guce_referrer_sig=AQAAAKxj_Bm_NFaQWyWT2RPd2P6rfBbWMgCJPbUxhqTn7Fdecu8HgfzTlbd86V4ExNaoyeVmJOyDI00RQGJkVgc3GmO0deVEaGXPwwHtHUmfgTD2i8_0zUWCCCvEL0XqanrNIoGWugj9sspmIZVEGtAT_e94HA6TxKExBNVohlcRDj_F&guccounter=2\">millennials who are becoming nuns</a></span>.</span></div><div><span><br></span></div><div><span>Scott Alexander on LGBT as a new civil religion, at <span><i><a href=\"https://slatestarcodex.com/2019/07/08/gay-rites-are-civil-rites/\">Slate Star Codex</a></i></span>.\u00a0 C. C. Pecknold on <span><a href=\"https://catholicherald.co.uk/commentandblogs/2019/07/02/the-pride-flag-flies-everywhere-but-what-does-it-stand-for/\">the phony neutrality of post-Obergefell liberalism</a></span>, at <i>Catholic Herald</i>.</span></div>"
},
"visual": {
"url": "http://b.vimeocdn.com/ts/452/218/452218069_1280.jpg",
"width": 1280,
"height": 720,
"contentType": "image/jpeg"
},
"unread": false,
"categories": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/5ca4d61d-e55d-4999-a8d1-c3b9d8789815",
"label": "Macintosh"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/category/fbdcd69b-7e27-4b6a-bfed-6584b944155d",
"label": "\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb\ud83e\udd1e\ud83c\udffb"
}
],
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
}
],
"actionTimestamp": 1571037184309
},
{
"keywords": [
"Google",
"Apple Arcade"
],
"originId": "https://www.macrumors.com/2019/09/26/apple-arcade-vs-google-play-pass/",
"fingerprint": "fd93a55d",
"id": "SusR11hdg5ydv7o+xGl+0XlI0AhBL77Oxid8QOO3j5k=_16d6f7df794:48e16:18991ffa",
"author": "Juli Clover",
"summary": {
"direction": "ltr",
"content": "With the launch of <a href=\"https://www.macrumors.com/roundup/ios-13/\">iOS 13</a>, <a href=\"https://www.macrumors.com/2019/09/19/apple-arcade-live-ios-13/\">Apple released Apple Arcade</a>, a new $4.99 per month gaming service that provides unlimited access to new and exclusive games.\n<br>\n<br>\nLess than two weeks later, Google <a href=\"https://www.macrumors.com/2019/09/23/google-launches-new-play-pass-service-on-android/\">announced its own gaming service</a> called Play Pass, which also offers unlimited access to games. In our latest YouTube video, we went hands-on with both services to compare them.\n<br>\n<br>\n<center><iframe allowfullscreen src=\"https://www.youtube.com/embed/klZfwC5Wh9I\" width=\"560\" height=\"315\"></iframe></center><center><em><a href=\"https://www.youtube.com/user/macrumors?sub_confirmation=1\">Subscribe to the MacRumors YouTube channel</a> for more videos.</em></center>\n<br>\nBoth <a href=\"https://www.apple.com/apple-arcade/\">Apple Arcade</a> and <a href=\"https://play.google.com/about/play-pass/\">Play Pass</a> are priced at $4.99 per month. Apple offers a one-month free trial while Google offers a 10-day free trial, but for the first year, Google is offering a deal that drops the price of Play Pass to $1.99 per month.\n<br>\n<br>\nThough the prices are similar, the two services are quite different. <a href=\"https://www.macrumors.com/guide/apple-arcade/\">Apple Arcade</a> features new and exclusive games, some of which were funded by Apple, while Google's Play Pass offers up older games.\n<br>\n<br>\nPlay Pass includes some super popular titles like Stardew Valley, Reigns, Star Wars Knights of the Old Republic, Terraria, and more, but the problem with older games is that many people may have already played them.\n<br>\n<br>\nPlay Pass is also not limited to games -- Google is including apps too. AccuWeather, Pic Stitch, ISS HD, and Tunable are some of the apps offered.\n<br>\n<br>\nApple Arcade only offers gaming titles, but all of the games are fresh, new, and exclusive to Apple Arcade. In some cases, though, some of the games are also available on consoles, but on mobile platforms, Apple Arcade gets exclusive access. That means no Android equivalent.\n<br>\n<br>\nIt's not clear if apps can leave Apple Arcade, but Google warns that apps can leave Play Pass. If that happens, users will need to buy the game to continue to use it if it's a paid game, and for free titles, ads and in-app purchases may show up.\n<br>\n<br>\nGoogle says there are hundreds of apps and games included with more being added every month. Apple Arcade launched with right around 60 games, and Apple has also promised new content on a monthly basis. Both services offer offline gaming, so no internet connection is required.\n<br>\n<br>\nWith both services, content is ad free and features no in-app purchases. Apple titles were designed from the ground up with no additional purchases, but for Play Pass, these gaming elements have been removed from titles that previously offered them.\n<br>\n<br>\nApple allows up to six family members to share games through a single Apple Arcade subscription using Family Sharing, and Google allows for up to five family members to share content through its Google Play Family Library.\n<br>\n<br>\nApple Arcade has launched in multiple countries around the world, while Play Pass is limited to the United States at the current time. Google does plan to expand, however.\n<br>\n<br>\nOne other aspect worth noting is privacy. Apple specifically mentions privacy protections and says that users are able to choose to share data, while Google's Play Pass materials don't mention privacy or data sharing. &quot;Every game must meet Apple's high privacy standards,&quot; reads Apple's <a href=\"https://www.apple.com/newsroom/2019/09/apple-arcade-invites-you-to-play-something-extraordinary/\">press release</a> for Apple Arcade.\n<br>\n<br>\nMost people are locked in to either Android or iOS and few have both, so most people won't need to choose between services.\n<br>\n<br>\nApple users who have a Mac, iPhone, <a href=\"https://www.macrumors.com/roundup/ipad/\">iPad</a>, or <a href=\"https://www.macrumors.com/roundup/apple-tv/\">Apple TV</a> can access Apple Arcade on those devices, while Google Play Pass requires a smartphone, laptop, or tablet with Android 4.4 or above and Play Store version 16.6.25. Google Play Pass games can't be played on the larger screen of a television, which gives Apple Arcade a bit of an edge when it comes to non-mobile gaming.\n<br>\n<br>\nDo you prefer Apple Arcade with its fresh selection of titles, or Google Play Pass with its larger library of already available games? Let us know in the comments.<br><br><div>Tags: <a href=\"https://www.macrumors.com/roundup/google/\">Google</a>, <a href=\"https://www.macrumors.com/roundup/apple-arcade/\">Apple Arcade</a></div><br>This article, &quot;<a href=\"https://www.macrumors.com/2019/09/26/apple-arcade-vs-google-play-pass/\">Apple Arcade vs. Google Play Pass</a>&quot; first appeared on <a href=\"https://www.macrumors.com\">MacRumors.com</a><br><br><a href=\"https://forums.macrumors.com/threads/apple-arcade-vs-google-play-pass.2201924/\">Discuss this article</a> in our forums<br><br><div>\n<a href=\"http://feeds.macrumors.com/~ff/MacRumors-Front?a=8qvKbZNFw68:13u-CPoeJ6s:yIl2AUoC8zA\"><img border=\"0\" src=\"http://feeds.feedburner.com/~ff/MacRumors-Front?d=yIl2AUoC8zA\"></a> <a href=\"http://feeds.macrumors.com/~ff/MacRumors-Front?a=8qvKbZNFw68:13u-CPoeJ6s:6W8y8wAjSf4\"><img border=\"0\" src=\"http://feeds.feedburner.com/~ff/MacRumors-Front?d=6W8y8wAjSf4\"></a> <a href=\"http://feeds.macrumors.com/~ff/MacRumors-Front?a=8qvKbZNFw68:13u-CPoeJ6s:qj6IDK7rITs\"><img border=\"0\" src=\"http://feeds.feedburner.com/~ff/MacRumors-Front?d=qj6IDK7rITs\"></a>\n</div><img width=\"1\" alt=\"\" src=\"http://feeds.feedburner.com/~r/MacRumors-Front/~4/8qvKbZNFw68\" height=\"1\">"
},
"alternate": [
{
"href": "https://www.macrumors.com/2019/09/26/apple-arcade-vs-google-play-pass/",
"type": "text/html"
}
],
"crawled": 1569533589396,
"title": "Apple Arcade vs. Google Play Pass",
"published": 1569531802000,
"origin": {
"streamId": "feed/http://www.macrumors.com/macrumors.xml",
"htmlUrl": "https://www.macrumors.com",
"title": "MacRumors: Mac News and Rumors - Front Page"
},
"visual": {
"url": "https://cdn.vox-cdn.com/thumbor/m3ztxUEeETYXD9gGfyEjUpS0GFE=/0x0:800x533/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59662701/800x_1.0.jpg",
"width": 1310,
"height": 873,
"contentType": "image/jpeg"
},
"unread": false,
"readTime": 33744,
"tags": [
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.read",
"label": ""
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.unsaved",
"label": "Unsaved"
},
{
"id": "user/f2f031bd-f3e3-4893-a447-467a291c6d1e/tag/global.saved",
"label": "Saved For Later"
}
],
"actionTimestamp": 1569536278861
}
]
}