diff --git a/RFCs/README.md b/RFCs/README.md index 4211ea6b..3858082d 100644 --- a/RFCs/README.md +++ b/RFCs/README.md @@ -13,8 +13,6 @@ the full playbook][rfc_playbook] for all the details! | Doc | Overview | | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| [[TODO] add title via yml front-matter to RFCs/\_dependency_rfc_template.md](/RFCs/_dependency_rfc_template.md#readme) | [TODO] add description via yml front-matter to RFCs/\_dependency_rfc_template.md | -| [[TODO] add title via yml front-matter to RFCs/\_generic_rfc_template.md](/RFCs/_generic_rfc_template.md#readme) | [TODO] add description via yml front-matter to RFCs/\_generic_rfc_template.md | | [[TODO] add title via yml front-matter to RFCs/on-call-incident-review.md](/RFCs/on-call-incident-review.md#readme) | [TODO] add description via yml front-matter to RFCs/on-call-incident-review.md | | [[TODO] add title via yml front-matter to RFCs/platform-practice-4ps.md](/RFCs/platform-practice-4ps.md#readme) | [TODO] add description via yml front-matter to RFCs/platform-practice-4ps.md | | [[TODO] add title via yml front-matter to RFCs/switch-to-standardrb.md](/RFCs/switch-to-standardrb.md#readme) | [TODO] add description via yml front-matter to RFCs/switch-to-standardrb.md | diff --git a/playbooks/README.md b/playbooks/README.md index 3638de6d..7dd65318 100644 --- a/playbooks/README.md +++ b/playbooks/README.md @@ -32,7 +32,6 @@ practice should be incorporated, submit a PR! | [Future Friday](/playbooks/future-friday.md#readme) | What is Future Friday? | | [GraphQL Schema Design](/playbooks/graphql-schema-design.md#readme) | What are our best practices for GraphQL Schema Design? | | [Hokusai](/playbooks/hokusai.md#readme) | a CLI to manage applications deployed to Kubernetes | -| [Incident Handling](https://www.notion.so/artsy/Incident-Handling-111cab0764a0808c993ec19b352cfab9?pvs=4#111cab0764a080f2a798e717a1610c46)πŸ”’ | How engineers share on-call duties and provide urgent support | | [How to give a good Informational](/playbooks/informationals.md#readme) | What are the steps needed to give someone a great experience. | | [Jira](/playbooks/jira.md#readme) | Working with Jira boards and tickets | | [Kubernetes](/playbooks/kubernetes.md#readme) | Deploying containerized applications at Artsy | diff --git a/playbooks/technology_radar/README.md b/playbooks/technology_radar/README.md index 6d21ac4b..e932e6af 100644 --- a/playbooks/technology_radar/README.md +++ b/playbooks/technology_radar/README.md @@ -5,7 +5,7 @@ https://radar.thoughtworks.com/?sheetId=https%3A%2F%2Fraw.githubusercontent.com% Moved an entry to `artsy-tech-radar-archive.csv` if the technology is no longer part of artsy's tech stack or we have assessed it should not be adopted. -NOTE: Order matters and there must be each of "adopt," "trial," "assess," and "hold" within the first quadrant or the radar will render rings out of order. +NOTE: Order matters and there must be each of "adopt," "trial," "assess," and "hold" within the first quadrant or the radar will render rings out of order. The script [sort-tech-radar-data.ts](../../scripts/sort-tech-radar-data.ts) sorts the current CSV. See the script itself for details. More information: - https://www.thoughtworks.com/radar/how-to-byor diff --git a/playbooks/technology_radar/artsy-tech-radar.csv b/playbooks/technology_radar/artsy-tech-radar.csv index dcd6e8b9..456ff69e 100644 --- a/playbooks/technology_radar/artsy-tech-radar.csv +++ b/playbooks/technology_radar/artsy-tech-radar.csv @@ -1,82 +1,82 @@ name,ring,quadrant,isNew,description -AI Code Assistants,assess,techniques,TRUE, "Tools like GitHub Copilot, Claude to assist with code generation and suggestions" -Apollo mcp,assess,languages & frameworks,TRUE,"Apollo MCP Server provides a standard way for AI models to access and orchestrate your APIs running with Apollo." -Apollo Server,assess,platforms,TRUE, -AWS Lambda,hold,platforms,TRUE, +easy-peasy,adopt,languages & frameworks,TRUE, +GraphQL,adopt,languages & frameworks,TRUE,"Appreciated for its flexibility and efficiency for clients." +JSON,adopt,languages & frameworks,FALSE,"We prefer JSON over most other data formats for its self-describing properties." +Python,adopt,languages & frameworks,FALSE,"For ETL, Analytics and ML workloads we use Python as a data engineering standard." +Rails,adopt,languages & frameworks,FALSE, +React Native,adopt,languages & frameworks,FALSE,"New iOS work should use React Native. See also Objective-C." +React.js,adopt,languages & frameworks,FALSE, +Relay,adopt,languages & frameworks,FALSE, +Ruby,adopt,languages & frameworks,FALSE,"Especially for data-backed APIs and services" +Spark,adopt,languages & frameworks,FALSE,"Used for data workloads, mostly via AWS EMR Serverless." +SQL,adopt,languages & frameworks,FALSE, +Swift,adopt,languages & frameworks,FALSE,"We generally opt for React Native, but when necessary for quality, usability or features tightly integrated with OS, we will build native features in Swift using UIKit. see Objective-C." +Typescript,adopt,languages & frameworks,FALSE, +UIKit,adopt,languages & frameworks,FALSE,"See Objective-C." AWS,adopt,platforms,FALSE, -Babel,hold,tools,TRUE,A JavaScript compiler +Docker,adopt,platforms,FALSE, +HSTS,adopt,platforms,FALSE, +Kubernetes,adopt,platforms,FALSE,"We manage staging and production clusters, and have solutions for logging, monitoring, alerting, scaling, and routing. See Hokusai and Substance repos." +Redis,adopt,platforms,FALSE, Bug Bounty Program,adopt,techniques,FALSE,For identifying and remediating security vulnerabilities with the help of external security researchers. Program overview at artsy.net/security. -Bun,assess,languages & frameworks,TRUE, runtime, A JavaScript runtime designed as a drop-in replacement for Node.js. Aims to be fast by using the Zig programming language and integrating a bundler, transpiler, package manager, and test runner." +Embeddings,adopt,techniques,TRUE,"Dense vector mathematical representation of content, suitable for semantic search" +Metaphysics' v2 schema,adopt,techniques,TRUE,"Consumers should switch to v2 rather than make further modifications to v1." +Opaque identifiers,adopt,techniques,FALSE,"Friendly identifiers or 'slugs' are fine for URLs but opaque, permanent identifiers are preferred for all internal or programmatic uses." +Partner API as a product,adopt,techniques,FALSE, "For our business partners to integrate with our platform. Documented at developers.artsy.net" +Split testing,adopt,techniques,TRUE,"When scale permits (such as around high-volume product surfaces or early funnel steps), we prefer to make data-driven product decisions by evaluating user experiences in parallel through A/B- or A/B/*-style tests" CircleCI,adopt,tools,FALSE,For continuous integration over other services like Travis or Semaphore. Shared orbs and contexts help our projects apply best practices. -Claude Pro,assess,tools,TRUE, "Anthropic's Claude Pro LLM service; provides higher usage limits and features compared to the free tier" Cloudflare,adopt,tools,TRUE, -Codepush,hold,tools,TRUE,"Microsoft codepush allows delivering updates over the air to javascript based mobile apps" -Coffeescript,hold,languages & frameworks,FALSE, Datadog APM,adopt,tools,FALSE, -Datadog Synthetics,assess,tools,TRUE, -Docker,adopt,platforms,FALSE, -easy-peasy,adopt,languages & frameworks,TRUE, -Elixir,hold,languages & frameworks,FALSE,A dynamic, functional language designed for building scalable and maintainable applications. -Embeddings,adopt,techniques,TRUE,"Dense vector mathematical representation of content, suitable for semantic search" -Expo Updates,trial,tools,TRUE,Allows for delivering over-the-air updates to React Native apps. -External LLM chatbot,hold,techniques,TRUE,"Risk of off-topic, unsafe, inaccurate responses (note: collector vs external)" -Fast agent,assess,languages & frameworks,TRUE,"lets you create and interact with sophisticated Agents and Workflows in minutes. It's multi-modal - supporting Images and PDFs in Prompts, Resources and MCP Tool Call results." Fastlane,adopt,tools,FALSE, -GitHub LLM tools,assess,tools,TRUE, GitHub's suite of tools for AI-assisted development, including Agents, Copilot, etc... -Google cloud,hold,platforms,FALSE, -GraphQL Stitching,hold,techniques,TRUE,"We prefer to connect to upstream REST APIs instead of GraphQL APIs." -GraphQL,adopt,languages & frameworks,TRUE,"Appreciated for its flexibility and efficiency for clients." HashiCorp Vault,adopt,tools,TRUE,Secrets management solution to securely store and access sensitive application information -Heroku,hold,platforms,FALSE,"We prefer kubernetes where we have shared solutions for authorization, logging, monitoring, alerting, scaling, and routing." -HSTS,adopt,platforms,FALSE, -Hypermedia,hold,techniques,FALSE,"While discoverable and easy to consume, we've found hypermedia APIs inefficient to consume." -Internal Agentic AI,assess,techniques,TRUE, "Internal development of AI agents to perform specific tasks autonomously within our systems. Monorepo at github.com/artsy/artsy-agent" -JSON,adopt,languages & frameworks,FALSE,"We prefer JSON over most other data formats for its self-describing properties." -Kubernetes,adopt,platforms,FALSE,"We manage staging and production clusters, and have solutions for logging, monitoring, alerting, scaling, and routing. See Hokusai and Substance repos." -LabFeatures (Gravity),hold,tools,TRUE,"Unleash supports similar feature-enabling on an individual account basis, while also being flexible enough for gradual roll-outs and split tests." Large Language Models,adopt,tools,TRUE,Deep learning models for text generation and embeddings -Machine facing LLM text generation,trial,techniques,TRUE,"Currently being used for GenomeBot and artwork captions" -Metaphysics' v1 schema,hold,techniques,TRUE,"The v1 schema is deprecated and should be considered frozen. Consumers should switch to v2 rather than make further modifications to v1." -Metaphysics' v2 schema,adopt,techniques,TRUE,"Consumers should switch to v2 rather than make further modifications to v1." -MongoDB,hold,tools,FALSE, -Next.js,hold,languages & frameworks,FALSE,"Currently trialing Next.js for our admin apps, via the pages router. See Forque, Volt 2. Some experiments have been made to migrate to Next 13's app router, which have all run into friction. See this Slack thread for more info." -Objective-C,hold,languages & frameworks,FALSE,"Because we use React Native some amount of Objective-C bridging code will remain necessary, these should be thin layers to swift features. Some legacy core Eigen infrastructure and React Native itself is written in Objective-C." -Opaque identifiers,adopt,techniques,FALSE,"Friendly identifiers or 'slugs' are fine for URLs but opaque, permanent identifiers are preferred for all internal or programmatic uses." OpenAI,adopt,tools,TRUE, "OpenAI's suite of LLM services and tools for ai assisted development and application features" OpenSearch,adopt,tools,FALSE,A community-driven, open source search and analytics suite derived from Elasticsearch -OpsGenie,hold,tools,TRUE,For incident management and alerting. Being retired by Atlassian. -Partner API as a product,adopt,techniques,FALSE, "For our business partners to integrate with our platform. Documented at developers.artsy.net" -Phoenix,hold,languages & frameworks,TRUE,Phoenix is a web development framework written in Elixir which implements the server-side Model View Controller (MVC) pattern. -Pingdom,hold,tools,TRUE, Postgresql,adopt,tools,FALSE,"We rely on Heroku or Amazon's RDS to host these databases. Though MongoDB was chosen to host core API data, in the long run we found the data (and maybe all data) to be more relational than document-oriented." -Public API as a product,hold,techniques,FALSE,For external developers to build on our platform. Deprecated with plan to retire. Documented at developers.artsy.net -Python,adopt,languages & frameworks,FALSE,"For ETL, Analytics and ML workloads we use Python as a data engineering standard." RabbitMQ,adopt,tools,FALSE,"Its stream allows source systems to publish their notable events and other systems to subscribe as desired. This avoids coupling systems directly. After evaluating Kafka for this purpose, we had more operational success with RabbitMQ." -Rails,adopt,languages & frameworks,FALSE, -React Native,adopt,languages & frameworks,FALSE,"New iOS work should use React Native. See also Objective-C." -React.js,adopt,languages & frameworks,FALSE, -Redis,adopt,platforms,FALSE, -Redux,hold,languages & frameworks,FALSE, -Relay,adopt,languages & frameworks,FALSE, Rsbuild,adopt,tools,FALSE,a build tool powered by Rspack. It aims for high performance and to provides an out-of-the-box setup to improve DX. -Ruby,adopt,languages & frameworks,FALSE,"Especially for data-backed APIs and services" -Scala,hold,languages & frameworks,FALSE, -Semaphore,hold,tools,FALSE, -Serverless architecture,hold,techniques,TRUE, Sidekiq,adopt,tools,FALSE, -SiteFeatures (Gravity),hold,tools,TRUE,"Site Features were used to turn on or off core functionality, but are easily replaced by Unleash (for platform-wide toggles) or Flipper (for Gravity-only settings)." -Spark,adopt,languages & frameworks,FALSE,"Used for data workloads, mostly via AWS EMR Serverless." -Split testing,adopt,techniques,TRUE,"When scale permits (such as around high-volume product surfaces or early funnel steps), we prefer to make data-driven product decisions by evaluating user experiences in parallel through A/B- or A/B/*-style tests" -SQL,adopt,languages & frameworks,FALSE, -Standard Ruby,trial,tools,TRUE,"After a few weeks living with this officially adopt." -Styled-components,hold,languages & frameworks,FALSE, -Swift,adopt,languages & frameworks,FALSE,"We generally opt for React Native, but when necessary for quality, usability or features tightly integrated with OS, we will build native features in Swift using UIKit. see Objective-C." Terraform,adopt,tools,FALSE, -Typescript,adopt,languages & frameworks,FALSE, -UIKit,adopt,languages & frameworks,FALSE,"See Objective-C." Unleash,adopt,tools,TRUE,"An open-source solution for feature-flagging, gradual roll-outs, and split-testing with SDKs for multiple languages" -User facing LLM text generation,hold,techniques,TRUE,"Risky without human-in-the-loop, due to concerns about accuracy, safety" -uv,assess,tools,TRUE,manages project dependencies and environments, with support for lockfiles, workspaces, and more, similar to rye or poetry +Machine facing LLM text generation,trial,techniques,TRUE,"Currently being used for GenomeBot and artwork captions" +Expo Updates,trial,tools,TRUE,Allows for delivering over-the-air updates to React Native apps. +Standard Ruby,trial,tools,TRUE,"After a few weeks living with this officially adopt." +Apollo mcp,assess,languages & frameworks,TRUE,"Apollo MCP Server provides a standard way for AI models to access and orchestrate your APIs running with Apollo." +Bun,assess,languages & frameworks,TRUE, runtime, A JavaScript runtime designed as a drop-in replacement for Node.js. Aims to be fast by using the Zig programming language and integrating a bundler, transpiler, package manager, and test runner." +Fast agent,assess,languages & frameworks,TRUE,"lets you create and interact with sophisticated Agents and Workflows in minutes. It's multi-modal - supporting Images and PDFs in Prompts, Resources and MCP Tool Call results." Vercel AI SDK,assess,languages & frameworks,TRUE,"Solid LLM abstractions in Typescript for text and data generation, with easily swappable models" +Apollo Server,assess,platforms,TRUE, +AI Code Assistants,assess,techniques,TRUE, "Tools like GitHub Copilot, Claude to assist with code generation and suggestions" +Internal Agentic AI,assess,techniques,TRUE, "Internal development of AI agents to perform specific tasks autonomously within our systems. Monorepo at github.com/artsy/artsy-agent" +Claude Pro,assess,tools,TRUE, "Anthropic's Claude Pro LLM service; provides higher usage limits and features compared to the free tier" +Datadog Synthetics,assess,tools,TRUE, +GitHub LLM tools,assess,tools,TRUE, GitHub's suite of tools for AI-assisted development, including Agents, Copilot, etc... +uv,assess,tools,TRUE,manages project dependencies and environments, with support for lockfiles, workspaces, and more, similar to rye or poetry +Coffeescript,hold,languages & frameworks,FALSE, +Elixir,hold,languages & frameworks,FALSE,A dynamic, functional language designed for building scalable and maintainable applications. +Next.js,hold,languages & frameworks,FALSE,"Currently trialing Next.js for our admin apps, via the pages router. See Forque, Volt 2. Some experiments have been made to migrate to Next 13's app router, which have all run into friction. See this Slack thread for more info." +Objective-C,hold,languages & frameworks,FALSE,"Because we use React Native some amount of Objective-C bridging code will remain necessary, these should be thin layers to swift features. Some legacy core Eigen infrastructure and React Native itself is written in Objective-C." +Phoenix,hold,languages & frameworks,TRUE,Phoenix is a web development framework written in Elixir which implements the server-side Model View Controller (MVC) pattern. +Redux,hold,languages & frameworks,FALSE, +Scala,hold,languages & frameworks,FALSE, +Styled-components,hold,languages & frameworks,FALSE, +AWS Lambda,hold,platforms,TRUE, +Google cloud,hold,platforms,FALSE, +Heroku,hold,platforms,FALSE,"We prefer kubernetes where we have shared solutions for authorization, logging, monitoring, alerting, scaling, and routing." +External LLM chatbot,hold,techniques,TRUE,"Risk of off-topic, unsafe, inaccurate responses (note: collector vs external)" +GraphQL Stitching,hold,techniques,TRUE,"We prefer to connect to upstream REST APIs instead of GraphQL APIs." +Hypermedia,hold,techniques,FALSE,"While discoverable and easy to consume, we've found hypermedia APIs inefficient to consume." +Metaphysics' v1 schema,hold,techniques,TRUE,"The v1 schema is deprecated and should be considered frozen. Consumers should switch to v2 rather than make further modifications to v1." +Public API as a product,hold,techniques,FALSE,For external developers to build on our platform. Deprecated with plan to retire. Documented at developers.artsy.net +Serverless architecture,hold,techniques,TRUE, +User facing LLM text generation,hold,techniques,TRUE,"Risky without human-in-the-loop, due to concerns about accuracy, safety" +Babel,hold,tools,TRUE,A JavaScript compiler +Codepush,hold,tools,TRUE,"Microsoft codepush allows delivering updates over the air to javascript based mobile apps" +LabFeatures (Gravity),hold,tools,TRUE,"Unleash supports similar feature-enabling on an individual account basis, while also being flexible enough for gradual roll-outs and split tests." +MongoDB,hold,tools,FALSE, +OpsGenie,hold,tools,TRUE,For incident management and alerting. Being retired by Atlassian. +Pingdom,hold,tools,TRUE, +Semaphore,hold,tools,FALSE, +SiteFeatures (Gravity),hold,tools,TRUE,"Site Features were used to turn on or off core functionality, but are easily replaced by Unleash (for platform-wide toggles) or Flipper (for Gravity-only settings)." Weaviate,hold,tools,TRUE,Vector storage database with semantic search (HNSW approximate nearest neighbor) and keyword search (BM25) -Webpack,hold,tools,FALSE,A static module bundler for JavaScript applications +Webpack,hold,tools,FALSE,A static module bundler for JavaScript applications \ No newline at end of file diff --git a/resources/README.md b/resources/README.md index f3a67c71..c286993e 100644 --- a/resources/README.md +++ b/resources/README.md @@ -8,7 +8,6 @@ Listicles, but on GitHub. |--|--| | [Links to the Art world](/resources/art.md#readme) | How to get up to date on the art world | | [Our highlight links about Artsy](/resources/artsy.md#readme) | An overview of links about Artsy and some of our bigger packaged Art content. | -| [Highlights from our engineering blog](/resources/blog.md#readme) | The best-of from our archives | | [Highlights from our Knowledge Shares](/resources/ks.md#readme) | The best-of from our archives | | [Leadership resources](/resources/leadership.md#readme) | The best-of from our [#leadership](https://artsy.slack.com/messages/leadership) πŸ”’ slack channel | | [How to get started on any of our tech stacks](/resources/tech-learning.md#readme) | The getting started guides | diff --git a/resources/art.md b/resources/art.md index cf19cec6..a9908fbf 100644 --- a/resources/art.md +++ b/resources/art.md @@ -9,8 +9,6 @@ description: How to get up to date on the art world - [1stdibs](https://www.1stdibs.com/) - [artnet](https://www.artnet.com/) -- [Artspace](https://www.artspace.com/) -- [Paddle8](https://paddle8.com/) ### Artsy Editorial must-reads @@ -26,13 +24,9 @@ description: How to get up to date on the art world ### News and Reviews - Of course, start with Artsy's [editoral coverage](https://www.artsy.net/articles) -- [Art F City](http://artfcity.com/) - [Art in America](https://www.artinamericamagazine.com/) - [Artforum](https://artforum.com/) - [artnet news](https://news.artnet.com/) -- [Artspace Magazine](https://www.artspace.com/magazine/) -- [Big Red & Shiny](https://bigredandshiny.org/) -- [BLOUIN ARTINFO](https://www.blouinartinfo.com/) - [Bomb](https://bombmagazine.org/) - especially artist interviews - [Brooklyn Rail](https://brooklynrail.org/) - [Carolina Miranda / LA Times](https://www.latimes.com/entertainment/arts/miranda/) @@ -59,13 +53,11 @@ You'll find many books in the 27th floor library, too. ### Art Calendars - [Artsy shows](https://www.artsy.net/shows) -- [Art Haps](https://www.arthaps.com/) - [Artcards](http://artcards.cc/) - [e-flux](http://www.e-flux.com/) - [Filterizer](http://www.filterizer.com/) - created by team member [Barry](https://github.com/bhoggard) - [NY Art Beat](http://www.nyartbeat.com/) - [WAGMAG](http://www.wagmag.org/) -- [zingrecsNY](http://www.zingmagazine.com/drupal/zingrecsNY) ### Podcasts @@ -81,18 +73,7 @@ You'll find many books in the 27th floor library, too. ### Miscellaneous - [Twitter list of Artsy team](https://twitter.com/artsy/lists/artsy-team) -- [Barry Hoggard & James Wagner on Building a Major Collection on a Budget](https://www.artspace.com/magazine/interviews_features/how_i_collect/how_i_collect_hoggard_and_wagner-51452) - - Barry works here, so ask him anything! -- Two part interview with collector Alain Servais: - [1](https://www.artspace.com/magazine/interviews_features/how_i_collect/alain-servais-interview-52794), - [2](https://www.artspace.com/magazine/interviews_features/how_i_collect/alain-servais-interview-part-2-52876) - Articles by art dealer [Edward Winkleman](https://twitter.com/WinklemanNYC) - - [The Myth of Disruptive Innovation in Online Art Selling Channels](http://www.edwardwinkleman.com/2015/05/the-myth-of-innovative-disruption-in.html) - - [10 Reasons You Should Buy Art](http://www.edwardwinkleman.com/2015/02/10-reasons-you-should-buy-art.html) - - [The Logic Behind the 50/50 Split](http://www.edwardwinkleman.com/2007/06/logic-behind-5050-split.html) - - [An Interview with Elizabeth Dee about the Contemporary Art Gallery Model, Part I](http://www.edwardwinkleman.com/2013/06/an-interview-with-elizabeth-dee-about.html) - - [A Conversation with Elizabeth Dee on the Contemporary Art Gallery Model, Part II](http://www.edwardwinkleman.com/2013/06/a-conversation-with-elizabeth-dee-on.html) - Articles by journalist [Felix Salmon](https://twitter.com/felixsalmon) - [The Not So Special Hundred-Million-Dollar Giacometti](https://www.newyorker.com/business/currency/the-hundred-million-dollar-giacometti) - - [A guide to the market oligopoly system](https://blogs.reuters.com/felix-salmon/2010/12/28/a-guide-to-the-market-oligopoly-system/) - [Jeff Koons: a master innovator turning money into art](https://www.theguardian.com/artanddesign/2014/jul/03/jeff-koons-master-innovator-whitney-money-art) diff --git a/resources/artsy.md b/resources/artsy.md index 96478395..512bb826 100644 --- a/resources/artsy.md +++ b/resources/artsy.md @@ -11,15 +11,8 @@ description: An overview of links about Artsy and some of our bigger packaged Ar - [what is Artsy?](/culture/what-is-artsy.md) - [give me just one article to read on Artsy](https://www.theverge.com/2017/7/18/15983712/artsy-fine-art-galleries-online-auction-sales) -- [what is it like to be an engineer at Artsy?](https://medium.com/artsy-blog/what-it-feels-like-to-work-in-a-supportive-environment-for-female-engineers-3c994a001007) - [where is our jobs page?](https://www.artsy.net/jobs) ### Art Content -- [Venice Bienale 2017 360Β° videos](https://www.artsy.net/venice-biennale/toward-venice) -- [Venice Bienale 2015](https://www.artsy.net/venice-biennale-2015), if anything watch - [this video](https://www.artsy.net/article/artsy-editorial-video-the-history-of-the-venice-biennale) -- [a video series about the art market in partnership with UBS](https://www.artsy.net/art-market-series) -- [2015 in the art world](https://www.artsy.net/article/artsy-editorial-2015-the-year-in-art) -- [2014 company report](https://2014.artsy.net/) -- [2013 company report](http://2013.artsy.net) +- [Artsy Editorial](https://www.artsy.net/articles) diff --git a/resources/blog.md b/resources/blog.md deleted file mode 100644 index 13a70ceb..00000000 --- a/resources/blog.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Highlights from our engineering blog -description: The best-of from our archives ---- - -## Series indexes - -- [Open Source by Default](https://artsy.github.io/series/open-source-by-default/) -- [JavaScriptures](https://artsy.github.io/series/javascriptures/) -- [Artsy Tech Stack](https://artsy.github.io/series/artsy-tech-stack/) -- [React Native at Artsy](https://artsy.github.io/series/react-native-at-artsy/) -- [Stages of Professional Growth](https://artsy.github.io/series/stages-of-professional-growth/) - -## Best of - -#### iOS / React Native - -- [How did we structure our React Native integration](https://artsy.github.io/blog/2016/08/24/On-Emission/) -- [How to make a React Native Pod](https://artsy.github.io/blog/2018/04/17/making-a-components-pod/) -- [How do we feel about Swift, now we're years down the line on TypeScript?](https://artsy.github.io/blog/2017/02/05/Retrospective-Swift-at-Artsy/) -- [What Cocoa patterns did we drop?](https://artsy.github.io/blog/2015/09/01/Cocoa-Architecture-Dropped-Design-Patterns/) -- [Why did we make our first closed-source pod?](https://artsy.github.io/blog/2014/06/20/artsys-first-closed-source-pod/) -- [How do you add a new key to CocoaPods Keys?](https://artsy.github.io/blog/2018/06/15/cocoapods-keys-react-native/) - -#### Web - -- [Where our stack is now](https://artsy.github.io/blog/2017/02/05/Front-end-JavaScript-at-Artsy-2017/) -- [How we OSS'd our web projects](https://artsy.github.io/blog/2016/09/06/Milestone-on-OSS-by-Default/) -- [How we handle legacy code](https://artsy.github.io/blog/2017/09/05/Modernizing-Force/) -- [The initial OSSing for our website](https://artsy.github.io/blog/2014/09/05/we-open-sourced-our-isomorphic-javascript-website) - -### GraphQL - -- [What is the future of GraphQL?](https://artsy.github.io/blog/2018/05/08/is-graphql-the-future/) - -### Culture - -- [What is the interviewing process at Artsy?](http://artsy.github.io/blog/2019/01/23/artsy-engineering-hiring/) -- [What is the interviewing process for a Junior?](https://artsy.github.io/blog/2016/01/30/iOS-Junior-Interviews/) -- [What is our engineering support process?](https://artsy.github.io/blog/2018/05/25/support-process/) -- [How do we automate stand-ups?](https://artsy.github.io/blog/2018/05/07/fully-automated-standups/) -- [When we ran a meetup with the Art + Feminism and WikiMedia folk](https://artsy.github.io/blog/2017/08/31/Editathon/) diff --git a/resources/ks.md b/resources/ks.md index b9c46b23..5cea5a79 100644 --- a/resources/ks.md +++ b/resources/ks.md @@ -6,29 +6,3 @@ description: The best-of from our archives Recordings of past Knowledge Shares (formerly "Lunch & Learns") can be found in the [shared Google Drive][google_drive_presentation_archive]. [google_drive_presentation_archive]: https://drive.google.com/drive/folders/1X8w7iFbdeVwi6v_xWLWfQdeJ81iewYWB?usp=sharing - -## Artsy's business - -- [Auctions](https://drive.google.com/drive/u/0/folders/1f4HlmOXEXKrI8Q6GeJzyl2uv9oDUYe-z) - πŸ”’ -- [Consignments](https://drive.google.com/drive/u/0/folders/1UlnwU_04pn2HoS7HtktD8pOYaii404Fc) - πŸ”’ -- [Gallery Ecosystem](https://drive.google.com/drive/u/0/folders/1PuveEt9Sq8lBi606TJepSqN5xrOjWGcq) - πŸ”’ - -## Folks at Artsy - -- [What is Product Marketing?](https://drive.google.com/drive/u/0/folders/1uvIPE8XvGC2_SfY4ZTWjKL6pgpyGQl65) - πŸ”’ - -## Tech - -- [GraphQL Best Practices](https://drive.google.com/drive/u/0/folders/1fqFKCR48hJjhEYdUbAURaxtbhsDu9tLI) - πŸ”’ -- [Danger & Peril](https://drive.google.com/drive/u/0/folders/1cAH1WTMp9DuZuU2F3kEkHlrqVu0Xwy8x) - πŸ”’ - -## DevOps - -- [Datadog and Kubernetes](https://drive.google.com/drive/u/0/folders/1l4Ikq-cN4lYxnDzjEwYpl0adf5578L06) - πŸ”’ diff --git a/resources/leadership.md b/resources/leadership.md index a97a003c..3c49c300 100644 --- a/resources/leadership.md +++ b/resources/leadership.md @@ -14,3 +14,4 @@ description: The best-of from our [#leadership](https://artsy.slack.com/messages - [Tribal Leadership by David Logan, John King, Halee Fisher-Wright](https://www.amazon.com/Tribal-Leadership-Leveraging-Thriving-Organization/dp/0061251321) - [The Five Dysfunctions of a Team by Patrick Lencioni](https://www.tablegroup.com/books/dysfunctions) +- [Radical Candor by Kim Scott](https://www.amazon.com/Radical-Candor-Revised-Kim-Scott/dp/1250258405/ref=sr_1_4?adgrpid=186026281403&dib=eyJ2IjoiMSJ9.K9bhjb5IR-wt4v7XygaHHvq6-UWMAq9AZZtYK527H6EwH4bALYz3y5ek9eD2fbQUf5x5x_HDDWxieKYqDejSsj2Mq8smeWb0vXjiGjdTdstGmku9C6LQYyOrsTQEBZ5-sf_vriLbhBgse-QkaAj2YjVua2F0NyNchsK7nlvBKgXbfUPhjtqIBcWB0SpxvVi2CLoXM1Hg6qPBIwoVn-NAGon_Y-DRmozP9xq6zu-w600.0PtOTRyVpJsjQpsMKW2u5iYgEwLLdSIDdPuLrUio7jQ&dib_tag=se&hvadid=779762256719&hvdev=c&hvexpln=0&hvlocphy=9009933&hvnetw=g&hvocijid=2263525392958371270--&hvqmt=e&hvrand=2263525392958371270&hvtargid=kwd-295322973806&hydadcr=24375_13859744_2336320&keywords=radical+candor&mcid=785b95afe11137ddaac3eee2ceaceadf&qid=1767033466&sr=8-4) diff --git a/resources/mobile/README.md b/resources/mobile/README.md deleted file mode 100644 index 507f675d..00000000 --- a/resources/mobile/README.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Artsy Mobile Learning Resources -description: Collections of further reading about iOS and, more specifically, iOS at Artsy. ---- - - - - - -- [Getting Started](#getting-started) -- [An Introduction for React Native Developers](#an-introduction-for-react-native-developers) -- [How to use Xcode](#how-to-use-xcode) -- [Fundamentals of Objective-C](#fundamentals-of-objective-c) -- [How is Artsy's iOS App Architected?](#how-is-artsys-ios-app-architected) - - - -Don't forget to check out the [Mobile Practice](../practices/mobile.md) for less technical, more organizational -resources. - -Materials for the iOS Learning Group (conducted mid-2019 at Artsy) can be found [here](./learning-group/README.md). - -## Getting Started - -There are three distinct ways of developing iOS software at Artsy: - -- Writing React Native components in [Emission](https://github.com/artsy/emission) (setup instructions in Readme) -- Writing Objective-C and Swift in [Eigen](https://github.com/artsy/eigen) (setup instructions in Readme), - sometimes importing React Native components _from_ Emission (as Objective-C `ARComponentController` subclasses). -- Developing React Native _and_ Objective-C and Swift using Emission and Eigen linked together, at the same time. - [Instructions are in the Eigen docs folder](https://github.com/artsy/eigen/tree/main/docs) - -A few important things to know: - -- Development and beta builds of the app appear on your homescreen as `Ξ”rtsy`. App Store builds appear as `Artsy`. -- The specific version of the app you have installed is on the debug menu of the app (shake your device). -- The bottom tab bar uses a purple top border while on staging, and a black top border on production. - -## An Introduction for React Native Developers - -React Native developers are really common at Artsy; any engineer who writes React can write React Native, and -therefore, can contribute to our iOS software. But for some tasks, React Native alone is not enough – you need some -native development skills. This section highlights some of the ways that our React Native and native (Objective-C) -code interoperate. - -The best resource to learn about the fundamentals of Artsy's app's interop is -[this blog post](https://artsy.github.io/blog/2016/08/24/On-Emission/). -[The Map to Emission](https://github.com/artsy/emission/blob/master/docs/map_to_emission.md) also contains an -[interop section](https://github.com/artsy/emission/blob/master/docs/map_to_emission.md#eigen-interop) that links -to further reading. - - - -## How to use Xcode - -The documentation on how to use Xcode is in the [xcode.md](./xcode.md) doc. - -## How to Find the Code - -Say you have some piece of the app and you're looking for the code that backs that UI. How do you do it? It can be -a little disorienting, but [instructions are here](./finding-code.md). - -## Fundamentals of Objective-C - - - -## How is Artsy's iOS App Architected? - -Artsy's app is split across [Emission](https://github.com/artsy/emission) (React Native components, some backed by -Objective-C) and [Eigen](https://github.com/artsy/eigen) (Swift and Objective-C). Eigen depends on Emission. You -can think of Emission as a component library, and Eigen routes between the components. - -Here are some further readings you can do to learn more about why we made these decisions: - -- [Our initial blog post on using React Native](https://artsy.github.io/blog/2016/08/15/React-Native-at-Artsy/) -- [Our 3 year retrospective on using React Native](https://artsy.github.io/blog/2019/03/17/three-years-of-react-native/) -- [A blog post on how routing works in Eigen](https://artsy.github.io/blog/2015/08/19/Cocoa-Architecture-Switchboard-Pattern/) - (predates our use of React Native but the concepts are unchanged) -- [The Map to Emission](https://github.com/artsy/emission/blob/master/docs/map_to_emission.md) is a list of the - different technologies Emission uses, with links to helpful PRs, blog posts, and representative code. diff --git a/resources/mobile/finding-code.md b/resources/mobile/finding-code.md deleted file mode 100644 index 99139453..00000000 --- a/resources/mobile/finding-code.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: How to Find Code For Some UI -description: Given some UI in the app, how does one find the code that backs that UI? ---- - -## Setup - -You need to have Xcode installed, running a version of Eigen ([here are the docs][getting_started]). You don't need -to have [Eigen and Emission linked](https://github.com/artsy/eigen/tree/main/docs) if you -just want to find the code – linking is only required if you want to debug/modify any React Native components in -Emission. - -## Finding the Code - -View controllers are the main unit of composition for iOS apps, and Artsy's app is no exception. Even our React -Native components are represented by view controllers. **Finding the code for some piece of user interface is -_really_ about finding its view _controller_**. We'll use Xcode's visual debugger for that. - -Build-and-run the app (the "play" button in Xcode, or ⌘R) and wait for it to launch in the simulator. In the -simulator, navigate to the UI that you're trying to find the code for. Then open Xcode's visual debugger (it's the -weird little icon near the bottom of the screen): - -![Xcode's visual debugger button](./images/xcode-open-visual-debugger.png) - -Xcode will show you a visual debugger. Click somewhere – anywhere, really – in the user interface that you're -trying to locate code for. When you select the element (single-click!) then the jump bar at the top of the screen -will show you the ancestors of the selected view. View _controllers_ are in orange. - -![Xcode's visual debugger](./images/xcode-visual-debugger.png) - -You're looking for the _right-most controller_. This is the "nearest ancestor" to the selected view. You can click -on the jump bar item for the controller to focus it. - -![Xcode's jump bar](./images/xcode-visual-debugger-focus.png) - -In this example, the controller is `ARHomeComponentViewController`. We can then use Xcode's "quick open" (βŒ˜β‡§O) to -navigate to the file quickly. If the code is in Eigen, we're done! If the code is in Emission (all React Native -controllers end with `ComponentViewController`) then we have to dig deeper. Let's continue the above example. - -Open the implementation file for the component controller (ie: `ARHomeComponentViewController.m`) and look for the -call to the `super`'s `init` method [`initWithEmission:moduleName:initialProperties`][init]. Note the second -parameter, `moduleName`. - -To get from the Objective-C controller to the React Native component, open Emission's [`AppRegistry.tsx` -file][registry] and look for the string literal that matches the `moduleName` (`"Home"`, in this case, located -[here][registry_code]). This will point you to the top-level React Native component that represents this controller -([here][code] in our example), and you can drill down the component hierarchy from here. - -[getting_started]: https://github.com/artsy/eigen/blob/master/docs/getting_started.md -[registry]: https://github.com/artsy/emission/blob/master/src/lib/AppRegistry.tsx -[registry_code]: - https://github.com/artsy/emission/blob/8ca8f40782ca9308fcb89a29ece2a84eef499388/src/lib/AppRegistry.tsx#L337 -[code]: https://github.com/artsy/emission/blob/master/src/lib/Scenes/Home/index.tsx -[init]: - https://github.com/artsy/emission/blob/8ca8f40782ca9308fcb89a29ece2a84eef499388/Pod/Classes/ViewControllers/ARHomeComponentViewController.m#L26-L28 diff --git a/resources/mobile/images/release-calendar.png b/resources/mobile/images/release-calendar.png deleted file mode 100755 index bbc6215b..00000000 Binary files a/resources/mobile/images/release-calendar.png and /dev/null differ diff --git a/resources/mobile/images/xcode-open-visual-debugger.png b/resources/mobile/images/xcode-open-visual-debugger.png deleted file mode 100644 index cdccf58b..00000000 Binary files a/resources/mobile/images/xcode-open-visual-debugger.png and /dev/null differ diff --git a/resources/mobile/images/xcode-visual-debugger-focus.png b/resources/mobile/images/xcode-visual-debugger-focus.png deleted file mode 100644 index af10f64c..00000000 Binary files a/resources/mobile/images/xcode-visual-debugger-focus.png and /dev/null differ diff --git a/resources/mobile/images/xcode-visual-debugger.png b/resources/mobile/images/xcode-visual-debugger.png deleted file mode 100644 index fa9531f0..00000000 Binary files a/resources/mobile/images/xcode-visual-debugger.png and /dev/null differ diff --git a/resources/mobile/learning-group/README.md b/resources/mobile/learning-group/README.md deleted file mode 100644 index 84acae3f..00000000 --- a/resources/mobile/learning-group/README.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: iOS Learning Group -description: Materials for the iOS Learning Group at Artsy ---- - -## Session One - -This week, we will cover how iOS software is developed, QA'd, and deployed. By the end of this session, all -participants should be able to pull the latest code from Eigen and Emission, see their work in an iOS simulator, -and link the two projects together. - -[Materials are here](./session-one.md). - -## Session Two - -This week, we will cover what makes React Native distinct from React on the web, as well as how Artsy leverages -shared infrastructure (such as Palette) to make it easier for engineers to work in either one. - -[Materials are here](./session-two.md). - -## Session Three - -This week, we will cover how to create a new view controller. View controllers are the main unit of composition for -native iOS apps, and we integrate our "Scene" React components as view controllers. This includes routing between -view controller, from both native Objective-C and React Native code. - -[Materials are here](./session-three.md). - -## Session Four - -This week, we will create our own React component to fit within the new view controller from Week 3. This will be a -Relay container, fetching data from Metaphysics v2. We will cover how to fetch data, how to re-fetch data, as well -as how Eigen and Emission integrate together to provide client-side API response caches (both Relay and others). - -[Materials are here](./session-four.md). - -## Session Five - -This is the final week. Participants are asked to bring an iOS bug from their product team's backlog that they -would like to fix as a group. - -[Materials are here](./session-five.md). diff --git a/resources/mobile/learning-group/images/session-four-example.png b/resources/mobile/learning-group/images/session-four-example.png deleted file mode 100644 index b08abfe3..00000000 Binary files a/resources/mobile/learning-group/images/session-four-example.png and /dev/null differ diff --git a/resources/mobile/learning-group/images/session-two-comparison.png b/resources/mobile/learning-group/images/session-two-comparison.png deleted file mode 100644 index e2d91670..00000000 Binary files a/resources/mobile/learning-group/images/session-two-comparison.png and /dev/null differ diff --git a/resources/mobile/learning-group/session-five.md b/resources/mobile/learning-group/session-five.md deleted file mode 100644 index 7a6ce470..00000000 --- a/resources/mobile/learning-group/session-five.md +++ /dev/null @@ -1,29 +0,0 @@ -# iOS Learning Group Session Five - -## Homework Review - -- Come prepared with a bug or feature from your team's backlog to work on. Session Five will be a working session, - where we'll either pair or mob as a group on these issues. -- We saw how React apps, and Emission in particular, avoid DRY software development. What do you think of this? - Why? -- Emission is currently the only client to use persisted queries. Why do you think Emission uses them while other - client apps (like Volt and Reaction) don't yet? - - **A**: Mobile apps often operate over cellular network connections, so sending only the query ID and variables - (rather than the whole query) saves a lot of bandwidth and provides a better UX. -- Extra credit: We see that the content of our component is touching the top edge of the simulator screen. This - makes sense, but poses a problem. What do you think the problem is, and how do you think we could fix it? Hint: - take a look around the Emission codebase for "SafeAreaInsets" and see what you find. - - **A**: The problem is the iPhone X-style notch. Use the `SafeAreaInsets` props to avoid it. - -## Mob Programming - -We mobbed on [PURCHASE-1545](https://artsyproduct.atlassian.net/browse/PURCHASE-1545) as a group, which resulted in -[this pull request](https://github.com/artsy/emission/pull/1944). Things we learned: - -- How to find the code for the Artwork component ([docs](https://artsyproduct.atlassian.net/browse/PURCHASE-1545)). -- How to type styling from Palette to keep our `ReadMore` component easy to use. -- How to update unit tests to reflect new behaviour in the component that's being tested. - -## Resources / Recommended Reading - -- [How to find code for a UI in iOS](https://artsyproduct.atlassian.net/browse/PURCHASE-1545) diff --git a/resources/mobile/learning-group/session-four.md b/resources/mobile/learning-group/session-four.md deleted file mode 100644 index 47166e19..00000000 --- a/resources/mobile/learning-group/session-four.md +++ /dev/null @@ -1,367 +0,0 @@ -# iOS Learning Group Session Four - -## Homework review - -- What is the purpose of a view controller, from a developer's perspective? What about from a user's perspective? - -
-Answer - -Recall from the last session that a view controller fits within the iOS Model-View-Controller framework. View -controllers are the glue between data models and user interfaces, mediating updates from the data models and user -interactions from the UI. A view controller is the main unit of composition for building native iOS UI's. From a -user's perspective, they might call it "a screen", since view controllers commonly take up the entire screen of an -app. However, view controllers really can be _composed_ so while a view controller might indeed take up the entire -screen, it might be composed of child controllers as the develop sees fit. - -
- -- How does a parameter on the Objective-C view controller `init` method get sent to the React component hierarchy - as a prop? - -
-Answer - -The parameter gets passed into the `initialProperties` parameter of the call to `super`'s -`initWithEmission:moduleName:initialProperties`. You need to add the prop to the component's `Props` type to access -it using TypeScript. -[Here are the docs](https://facebook.github.io/react-native/docs/native-modules-ios#argument-types) that describe -translating Objective-C types into JavaScript types. - -
- -- We've seen how to create new controllers and link them with Eigen. Routing between view controller is all - URL-based; what are the advantages and disadvantages to this approach? - -
-Answer - -**Advantages**: Getting to use the same URLs as the web means push notifications, deep links, and email deep links -all use the same routing infrastructure as UI interactions. We also get to fall back to web views for URLs that the -app doesn't handle. - -**Disadvantages**: Data passed from one controller to another has to be serialized into a URL first. Common -patterns such as passing `delegate` references or forwarding completion handlers to new view controllers is not -possible. - -
- -- Related: in `ARSwitchBoard.m`, you'll see two different methods for adding routes: `addRoute:handler:` and - `registerEchoRouteForKey:handler:`. This lets us define routing URLs in [Echo](https://github.com/artsy/echo), - Artsy's feature-flag-as-a-service. Why do you think this functionality was added here? - -
-Answer - -This was added very early so that Force could update its URLs and Eigen could be updated remotely to match the new -scheme. It's never been used, though. - -
- -- Extra credit: Emission's `ARComponentViewController` defines a few props that get passed down based on iOS View - Controller lifecycle methods. What are they? (Hint: [here is a PR](https://github.com/artsy/emission/pull/1890) - that uses them.) - -
-Answer - -The `isVisible` prop is passed to all top-level components (ie: components backed by a component view controller). -This lets the component know if it's currently visible to the user or not. - -
- - - -## Making a Relay Component - -Last week we built the following `MyNewComponent`: - -```tsx -export class MyNewComponent extends React.Component { - render() { - return Hello world! - } -} -``` - -This doesn't really do anything. This week, we're going to look at turning this into a **Relay component** that -fetches actual data from Metaphysics v2. To keep things simple, **we will be staying totally within the Emission -test app**. - -Let's start by checking out the code from the end of last session: - -```sh -# cd into Emission's repo -git fetch --tags # Required! -git checkout ios-learning-session-three-finished -git checkout -b ios-learning-group-session-four -``` - -Okay, let's start turning this into a Relay component. It's an iterative process, and we're going to work on small -pieces at a time. Let's apply the following diff to `MyNewComponent.tsx`: - -```diff - import { Serif } from "@artsy/palette" - import React from "react" -+import { createFragmentContainer, graphql, RelayProp } from "react-relay" - --export class MyNewComponent extends React.Component { -+interface Props { -+ relay: RelayProp -+} -+ -+export class MyNewComponent extends React.Component { - render() { - return Hello world! - } - } -+ -+export const MyNewComponentFragmentContainer = createFragmentContainer( -+ MyNewComponent, -+ { -+ artist: graphql` -+ fragment MyNewComponent_artist on Artist { -+ name -+ } -+ `, -+ } -+}) -``` - -This wraps the existing `MyNewComponent` in a fragment container. Since Reaction and Emission are both built with -Relay, this code is nearly identical across the web and React Native platforms. - -Next we need to get the Relay compiler to generate some types for us. If you don't have `yarn start` running, then -you can run `yarn relay` instead to generate them. Then, we can import the type and start using the prop: - -```diff - import { Serif } from "@artsy/palette" - import React from "react" - import { createFragmentContainer, graphql, RelayProp } from "react-relay" -+import { MyNewComponent_artist } from "__generated__/MyNewComponent_artist.graphql" - - interface Props { -+ artist: MyNewComponent_artist - relay: RelayProp - } - - export class MyNewComponent extends React.Component { - render() { -- return Hello world! -+ return Hello {this.props.artist.name} - } - } -``` - -Cool! So you can see the workflow is very similar to Reaction: - -1. Wrap a component in a Relay fragment container with a defined GraphQL fragment. -2. Run `yarn relay` (or keep `yarn start` running, which runs Relay in watch mode) to generate the types. -3. Import the types and add the prop you're fetching. -4. Use the prop. - -Okay so let's do a little more work here to get the component rendered from native code. Remember, we're just -sticking to Emission for now, there would be extra steps to integrate this into Eigen -([here is an example commit](https://github.com/artsy/eigen/commit/189f55eb15538af32f1fb910f0830e999fddca6b)). - -Next step: we need a query renderer. This can get a little confusing because we have _yet another_ layer of -indirection. Here's how you can think of the various pieces: - -``` -MyNewComponentQueryRenderer <- Defines how to fetch the data. -MyNewComponentFragmentContainer <- Defines what data to fetch. -MyNewComponent <- Defines what to do with the data once its fetched. -``` - -Most query renderers look very similar (because we've structured our code to make them similar, and boring). It's -not [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) but it has a lot of benefits, like how everything -is explicit and doesn't rely on developers having the right context to understand how things work. So let's write -our query renderer. - -Query renderers used to be all defined in -[their own file](https://github.com/artsy/emission/blob/master/src/lib/relay/QueryRenderers.tsx) but the common -practice now is to define them in the same file as the component and fragment container. Add the following to the -end of `MyNewComponent.tsx`: - -```tsx -export const MyNewComponentQueryRenderer: React.SFC<{ artistID: string }> = ({ artistID, ...others }) => { - return ( - { - return ( - - ) - }} - /> - ) -} -``` - -There's a lot going on here, but the really important part is this line: - -```tsx -render = { renderWithLoadProgress(MyNewComponentFragmentContainer, others) } -``` - -`renderWithLoadProgress` shows a spinner for the user while the query is fetched from Metaphysics, which is really -nice. - -If your VSCode is setup to automatically apply ESLint fix-its, then this next step might be done automatically. We -use a lint rule to automatically apply a TypeScript generic to `QueryRenderer`. You can see the diff here: - -```diff - import React from "react" - import { createFragmentContainer, graphql, RelayProp, QueryRenderer } from "react-relay" - import { MyNewComponent_artist } from "__generated__/MyNewComponent_artist.graphql" -+import { MyNewComponentQuery } from "__generated__/MyNewComponentQuery.graphql" - import { RetryErrorBoundary } from "./RetryErrorBoundary" - import { defaultEnvironment } from "lib/relay/createEnvironment" - import renderWithLoadProgress from "lib/utils/renderWithLoadProgress" -@@ -40,7 +41,7 @@ export const MyNewComponentQueryRenderer: React.SFC<{ artistID: string }> = ({ a - { - return ( -- - environment={defaultEnvironment} - query={graphql` - query MyNewComponentQuery($artistID: String!) { -``` - -Okay! We're all set, we just need to define the prop in our Objective-C class, `MyNewComponentViewController`. Open -`MyNewComponentViewController.h` and make the following change: - -```diff - @interface ARMyNewComponentViewController : ARComponentViewController - --- (instancetype)init; -+- (instancetype)initWithArtistID:(NSString *)artistID; - -+ @property (nonatomic, readonly) NSString *artistID; - - @end -``` - -And make a similar change to the `.m` file: - -```diff - @implementation ARMyNewComponentViewController - --- (instancetype)init; -+- (instancetype)initWithArtistID:(NSString *)artistID; - { -- return [super initWithEmission:nil -- moduleName:@"MyNewComponent" -- initialProperties:@{}]; -+ if ((self = [super initWithEmission:nil -+ moduleName:@"MyNewComponent" -+ initialProperties:@{ @"artistID": artistID }])) { -+ _artistID = artistID; -+ } -+ return self; - } -``` - -This change will inject the `artistID` as an initial property, which is used by the `QueryRenderer` and passed -along to Metaphysics as a query variable. Cool! If you opened Emission's Example app in Xcode, you'll see that it -will fail to compile because we aren't using the new `artistID` parameter yet. We'll need to make a change to our -`ARRootViewController.m` file to use this new parameter: - -```diff - - (ARCellData *)jumpToMyNewComponent - { - return [self tappableCellDataWithTitle:@"My New Component" selection: ^{ -- [self.navigationController pushViewController:[[ARMyNewComponentViewController alloc] init] animated:YES]; -+ [self.navigationController pushViewController:[[ARMyNewComponentViewController alloc] initWithArtistID:@"tim-fishlock"] animated:YES]; - }]; - } -``` - -For Emission's test app, it's okay to use hardcoded IDs like this. The app is just to help us test, afterall, so we -can change these to whatever artist we want. - -Finally, we need to rewire the `AppRegistry` to point to the new query renderer: - -```diff --import { MyNewComponent } from "./Components/MyNewComponent" -+import { MyNewComponentQueryRenderer as MyNewComponent } from "./Components/MyNewComponent" -``` - -If you recompile the app in Xcode and tap the "MyNewCompent", you sould see the following: - -![Screenshot of simulator](./images/session-four-example.png) - -Awesome! Our next steps are some cleanup, and then to deploy Emission and update Eigen. But we're skipping the -deploy step for this tutorial, so let's do some cleanup. - -You'll recall from the last session that we created a Storybook entry for `MyNewComponent`. Let's update -`MyNewComponent.story.tsx` to work with the new query renderer. - -```diff - import { storiesOf } from "@storybook/react-native" - import React from "react" --import { MyNewComponent } from "../MyNewComponent" -+import { MyNewComponentQueryRenderer as MyNewComponent } from "../MyNewComponent" - --storiesOf("MyNewComponent").add("Show default component", () => { -- return -+storiesOf("MyNewComponent").add("Show Tim Fishlock", () => { -+ return - }) -``` - -And that's it! Completed code from this session can be found in Emission's repo at -[the `ios-learning-group-session-four-finished` tag](https://github.com/artsy/emission/releases/tag/ios-learning-group-session-four-finished). - -## What about the Query Map? - -When ran in development mode, Emission sends the full GraphQL query and variables to Metaphysics. However, deployed -versions (App Store releases **and** betas!) don't do this. Instead, they use GraphQL persisted queries. -Metaphysics has a -[flat JSON file](https://github.com/artsy/metaphysics/blob/9b782b05e9e7da08937581fddb4ef1cf315e2136/src/data/complete.queryMap.json) -that defines every query of every version of Emission ever released. When a new version of Emission is made, the -release script automatically generates a new query map, merges it with the existing one, and opens a pull request -([example](https://github.com/artsy/metaphysics/pull/1993)). - -(In the longterm, we plan to move away from a flat file for persisted queries. This will unlock other clients to -use the persisted queries, too, since Emission is currently the only one.) - -Until this PR is merged, the updated query map won't be deployed to staging. That means that the build of Emission -won't work at all. Once deployed to staging, the build will work _only on staging_. Until we promote Metaphysics to -production, queries will fail on production. **This is a common source of bug reports** from colleagues when we cut -a new beta. If you release Emission, make sure to communicate with your team and in the #practice-mobile Slack -channel. - -## Core Concept Review & Homework - -- Come prepared with a bug or feature from your team's backlog to work on. Session Five will be a working session, - where we'll either pair or mob as a group on these issues. -- We saw how React apps, and Emission in particular, avoid DRY software development. What do you think of this? - Why? -- Emission is currently the only client to use persisted queries. Why do you think Emission uses them while other - client apps (like Volt and Reaction) don't yet? -- Extra credit: We see that the content of our component is touching the top edge of the simulator screen. This - makes sense, but poses a problem. What do you think the problem is, and how do you think we could fix it? Hint: - take a look around the Emission codebase for "SafeAreaInsets" and see what you find. - -## Resources / Recommended Reading - -- [Relay docs](https://relay.dev) -- [JavaScriptures session on Relay](https://artsy.github.io/blog/2018/06/13/JavaScriptures-4.1-Relay/) diff --git a/resources/mobile/learning-group/session-one.md b/resources/mobile/learning-group/session-one.md deleted file mode 100644 index a1ffad6e..00000000 --- a/resources/mobile/learning-group/session-one.md +++ /dev/null @@ -1,151 +0,0 @@ -# iOS Learning Group Session One - -Before we talk about iOS software, we're going to get started on some long-running tasks to get you set up with -local development environments. We'll bounce back and forth between this section and the next, as these -long-running tasks allow us. - -## Before Arriving - -- Download **Xcode 10.3** from [Apple's developer tools portal](https://developer.apple.com/download/more/) (login - required, use your personal Apple ID) - - It's important not to download Xcode from the App Store because it will auto-update, and we want to pin our - developer tools. -- Unzip the download, it will take a while -- Install [CocoaPods](https://cocoapods.org) - - `gem install cocoapods` -- Install the [React Native Debugger](https://github.com/jhen0409/react-native-debugger) - - `brew update && brew cask install react-native-debugger` - -## Setup - -First, let's clone Eigen and get it set up. Eigen is the native parts of our app (Objective-C and Swift). - -```sh -git clone https://github.com/artsy/eigen.git -cd eigen -bundle install -make artsy -bundle exec pod install --repo-update # You'll be asked for keys here. -open Artsy.xcworkspace -``` - -> **Q**: What happens if `bundle exec pod install` fails to run? -> -> **A**: Your first step should always be to try re-running it, it often works! - -> **Q**: What if I have more than on Xcode installed? -> -> **A**: You can use `xcode-select` to switch between versions of Xcode _used for the command line_. This is -> critical for command-line use, since which Xcode you use defines which version of `gcc`, `git`, and other tools -> you'll use. Make sure it's pointed to `Xcode.app` and **not** a subdirectory, like -> `Xcode.app/Contents/Developer`. - -This looks kind of weird, so let's break it down. First we clone the repo, then `cd` into it, then -`bundle install`. This is because our iOS dependency manager, [CocoaPods](https://cocoapods.org), is itself a -RubyGem. So we need to use Bundler to install CocoaPods. Then `make artsy` does some neat stuff with git, and then -finally we install our iOS dependencies and open the Xcode workspace. - -`pod install` is what installs the actual iOS dependencies (called "pods"). Because of how CocoaPods works, it has -to cache all known pod specs on your disk, which takes a while the first time. Pods are installed into the `Pods` -directory, similar to `node_modules`. - -> **Q**: When to run a repo update? -> -> **A**: If `bundle exec pod install` fails because it can't resolve a dependency (usually Emission) then try -> adding `--repo-update`. - -During `pod install`, you'll be asked for keys (API keys, etc). Look for the "Eigen Keys" secure note in the -1Password vault. We use a CocoaPods plugin that Orta wrote, -[cocoapods-keys](https://www.github.com/orta/cocoapods-keys), to store keys securely in the macOS keychain. During -`pod install`, it will obfuscate them to be compiled into the binary (more on that later). - -> **Q**: What if I enter a key incorrectly? -> -> **A**: You can interact with the key store through the CocoaPods-Keys plugin. Run `bundle exec pod keys --help` -> for a list of commands, but you'll probably want to _set_ a key. Check out usage with -> `bundle exec pod keys set --help`. - -After that, you should be able to open the Xcode workspace and hit ⌘R to launch the app in a simulator. - -Alright now let's do Emission, which is the React Native parts of our app. - -``` -git clone https://github.com/artsy/emission.git -cd emission -yarn install # Install JS dependencies -cd Example -bundle install -bundle exec pod install # Installs CocoaPods -open Emission.xcworkspace -cd .. -yarn start # Starts React Native packager -``` - -This clones the repo, installs the JavaScript dependencies, and then installs the CocoaPods for the Emission -Example app. So what's that? - -Emission is kind of complicated. It's three things in one: - -- An npm module (our React Native components). -- A CocoaPod (Objective-C wrappers for our React Native components). -- An Example app (a testbed for our components). - -You'll be asked for keys again during `pod install`. - -Emission and Eigen can be confusing, not least because they both start with the letter 'e'. Eigen is what we work -in for native code, and Emission for React Native components. Sometimes we link them together, but it's often -easier to just develop in them independently. - -## What is iOS, Even? - -Okay so while that's all happening, we can talk a bit about how iOS software works, and how that's different from -the web. - -Web apps get served directly to users' browsers. The browser fetches the various JavaScript assets needed to -execute the app. If we want to update the web app, we just serve different assets to the browser. Rollbacks are -easy, since we just serve different assets. - -iOS apps get submitted to Apple for App Store review, where Apple vets them for App Store guideline adherence. Once -satisfied (a day or so), Apple will then let users download the app. So there's two key differences here: - -1. There's an intermediary (Apple) involved in deploying an iOS app. -2. Users aren't served the app, they download the binary executable. - -Because users are downloading our app entirely, as an executable, we can't just switch to serving different assets -for deploys or rollbacks. **Any change we make takes at least a day to get into our users' hands**. Furthermore, -once the executable is installed, users never have to update it. **Users can keep using old versions of our app**, -and they frequently do. - -No rollbacks and day-long deploys mean that QA is a lot more serious for iOS apps, which we -[document in Notion](https://www.notion.so/artsy/514e1e1c55604b1890f678c748d4223a?v=fb0bcb2e9e9d4d07afefb05a64cd371b). - ---- - -Okay, so how do we get Eigen and Emission talking to each other? Since Eigen is the consumer of Emission's -components, we'll run Eigen and get it to work with our local Emission. - -There are two levels of doing this. If you just need to link React Native changes, then it's easy to point Eigen to -the local React Native Packager (in the debug menu). If you're also making Objective-C changes to Emission and want -to see them reflected in Eigen, it's a little trickier. Both options are -[detailed in the documentation](https://github.com/artsy/eigen/tree/main/docs). - -## Core Concept Review & Homework - -Here are some questions to take away with you and ponder. Try to answer them, we'll review them next week. - -- iOS Software is really different from web software: we ship a binary instead of serving a web page. - What other - kinds of software have the same constraints? - - We talked today about the disadvantages of iOS' approach to deployments, but can you think of any benefits? -- Come up with an idea for Artsy's iOS app. A feature, bug fix, a whole new screen, whatever! Think about how and - where the code would be to implement it. -- Extra credit: look at the Palette repo and find a component that works in both React (web) and React Native. Find - one that only works on one. Why do you think these limitations exist? - -## Resources / Recommended Reading - -- Eigen: [https://github.com/artsy/eigen](https://github.com/artsy/eigen) -- Emission: [https://github.com/artsy/emission](https://github.com/artsy/emission) -- Linking Emission and Eigen: - [https://github.com/artsy/eigen/blob/master/docs/using_dev_emission.md](https://github.com/artsy/eigen/blob/master/docs/using_dev_emission.md) -- Apple developer tools download page (login required): - [https://developer.apple.com/download/more/](https://developer.apple.com/download/more/) diff --git a/resources/mobile/learning-group/session-three.md b/resources/mobile/learning-group/session-three.md deleted file mode 100644 index 98266676..00000000 --- a/resources/mobile/learning-group/session-three.md +++ /dev/null @@ -1,144 +0,0 @@ -# iOS Learning Group Session Three - -## Homework review - -- What are some of the constraints on mobile devices that aren't present on web browsers? What about mobile - browsers, did they also have these constraints? Do they still have them? -- We've seen how Palette components differ across iOS and web, like the `EntityHeader`. How do you think this - affects the Relay fragments we use for this component? -- Recall the idea you came up with for the app, from last week's homework. Considering that features in the app are - built in one of three places (native code, React Native code, or a web view), where do you think Artsy should put - build your idea? Why? -- Extra credit: we briefly looked at how to call a function in Objective-C. How do you think that function is - defined at the _implementation_ site? - -
-Answer - -```objc -// Given this call site: -[self.relay refetch:params renderVariables:nil]; - -// What would the implementation look like? -- (void)refetch:(RefetchParameters *)params renderVariables:(NSDictionary *)renderVariables; -``` - -
- -## View Controllers - -In native iOS applications, -[view controllers](https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/) are -the main unit of composition for writing application code. Some view controllers are primarily responsible for -managing other view controllers, like `UINavigationController`. Its job is to manage a stack of view controllers so -that the user can push new view controllers onto the stack (through navigation UI actions) and pop view controllers -off of the stack (by pressing the back button). Another example is the tab controller, which is responsible for -switching view controllers based on the user's selected tab. Eigen uses both of these heavily. - -While many iOS applications now use non-MVC frameworks, view controllers are still a core part of the iOS SDK. They -are responsible for a view hierarchy, kind of like a React component hierarchy. - -What we do with Eigen and Emission is kind of sneaky. Recall that Emission is imported as a CocoaPod into Eigen, -and that Emission exports a bunch of view controllers for Eigen to use. From Eigen's perspective, these are regular -view controllers, even though they have React Native view hierarchies. - -Whenever you want to display something to the user in Emission, it needs to ultimately be inside a view controller. -If you're adding a whole new screen, then you'll need to add a new view controller. And that means writing some -Objective-C. - -## Creating a new Emission View Controller - -(**Note**: You can find the Emission code after these changes at the -[`ios-learning-session-three-finished` tag](https://github.com/artsy/emission/tree/ios-learning-session-three-finished).) - -Instead of outlining all the steps to take to build a new view controller in Emission, we're going to -[rely on the documentation](https://github.com/artsy/emission/blob/master/docs/adding_new_components.md). We're -going to follow all the steps here, up to (but not including) "Next Steps", and here's why. - -Normally when we add a new controller, we want to actually use it within Eigen. This is cumbersome for tutorials, -since we don't _actually_ want to add the `ARMyNewComponentViewController` controller. However, what we'll do is -get Eigen and Emission linked together, so we can see how they two work together without actually having to deploy -a new version of Emission. - -Make sure that Emission's packaging server is running (`yarn start`). Then go to Eigen's `Podfile` and make the -following change: - -```diff -- pod 'Emission', '~> 1.17' -+ pod 'Emission', path: '../emission' -``` - -Then re-run `bundle exec pod install`. This tells CocoaPods to use the Emission `podspec` that's defined in the -local `path` (instead of [the one in Artsy's Specs repo](https://github.com/artsy/Specs/tree/master/Emission)). -**Important**: Changes to existing Objective-C/React files are reflected _immediately_, while **newly created or -deleted files** will need to have `pod install` ran _first_ before the changes get picked up. - -Now we need to set up routing to our new controller. We're going to be taking over the tap for any artwork, to -navigate to our new controller. Find Emission's `ArtworkGridItem.tsx` file and locate -[the component's `handleTap` method](https://github.com/artsy/emission/blob/ac291bbbc5774b7fefa9718f384d6cfecf14220b/src/lib/Components/ArtworkGrids/ArtworkGridItem.tsx#L49-L54). -Make the following change: - -```diff - handleTap() { -- // FIXME: Should this be internalID? -- this.props.onPress && this.props.artwork.slug -- ? this.props.onPress(this.props.artwork.slug) -- : SwitchBoard.presentNavigationViewController(this, this.props.artwork.href) -+ SwitchBoard.presentNavigationViewController(this, "/my-new-component") -+ // // FIXME: Should this be internalID? -+ // this.props.onPress && this.props.artwork.slug -+ // ? this.props.onPress(this.props.artwork.slug) -+ // : SwitchBoard.presentNavigationViewController(this, this.props.artwork.href) - } -``` - -What we've done is comment out its implementation and instead, always try to route to `"/my-new-component"`. Let's -set up the routing for this in Eigen. - -Open Eigen's `ARSwitchBoard.m` file (βŒ˜β‡§O in Xcode). This defines all the routes, based on URL's (like -`"/my-new-component"`). Make the following change: - -```diff - #import - #import - #import -+#import - - #import "ArtsyEcho.h" - #import "Artsy-Swift.h" -@@ -342,6 +343,10 @@ - (void)updateRoutes - return [sself loadUnknownPathWithID:parameters[@"slug"]]; - }]; - -+ [self.routes addRoute:@"/my-new-component/:text" handler:JLRouteParams{ -+ return [[ARMyNewComponentViewController alloc] init]; -+ }]; -+ - // The menu items' paths are added in ARTopMenuViewController - } -``` - -You can look at other examples -([like this one](https://github.com/artsy/eigen/blob/6782e612174d27206c2826f05a24c3ac6f25060a/Artsy/App/ARSwitchBoard.m#L218-L221)) -for how to pass parameters through the URL into the new controller (to be used as props). - -Recompile Eigen and tap any artwork in any artwork grid; it should route to your new component. - -## Core Concept Review & Homework - -- What is the purpose of a view controller, from a developer's perspective? What about from a user's perspective? -- How does a parameters on the Objective-C view controller `init` method get sent to the React component hierarchy - as a prop? -- We've seen how to create new controllers and link them with Eigen. Routing between view controller is all - URL-based; what are the advantages and disadvantages to this approach? -- Related: in `ARSwitchBoard.m`, you'll see two different methods for adding routes: `addRoute:handler:` and - `registerEchoRouteForKey:handler:`. This lets us define routing URL's in [Echo](https://github.com/artsy/echo), - Artsy's feature-flag-as-a-service. Why do you think this added functionality here? -- Extra credit: Emission's `ARComponentViewController` defines a few props that get passed down based on iOS View - Controller lifecycle methods. What are they? (Hint: [here is a PR](https://github.com/artsy/emission/pull/1890) - that uses them.) - -## Resources / Recommended Reading - -- [View Controller Programming Guide for iOS](https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/) -- [Adding a New Component](https://github.com/artsy/emission/blob/master/docs/adding_new_components.md) diff --git a/resources/mobile/learning-group/session-two.md b/resources/mobile/learning-group/session-two.md deleted file mode 100644 index 691ef9d6..00000000 --- a/resources/mobile/learning-group/session-two.md +++ /dev/null @@ -1,346 +0,0 @@ -# iOS Learning Group Session Two - -## Homework review - -- iOS Software is really different from web software: we ship a binary instead of serving a web page. - What other - kinds of software have the same constraints? - We talked today about the disadvantages of iOS' approach to - deployments, but can you think of any benefits? -- Come up with an idea for Artsy's iOS app. A feature, bug fix, a whole new screen, whatever! Think about how and - where the code would be to implement it. - -## Exercise: Let's pull latest changes! - -Over the past week, there have been changes to both the Eigen and Emission repositories. Let's pull from each and -make sure we're able to launch the latest code in our simulators. - -> **Reminder**: Eigen and Emission use _branches_ on Artsy's repo, and **not** forks. Submit pull requests to -> Artsy's branch, with a good descriptive name. - -### Eigen - -Remember that Eigen uses CocoaPods, which is itself managed through Bundler. You might need to update your gems -_first_ and then your CocoaPods. - -```sh -# cd into eigen directory -git pull -bundle install # if necessary -bundle exec pod install # might need to add --repo-update if it fails -# Open workspace -``` - -### Emission - -Emission has [pull-lock](https://github.com/orta/pull-lock) configured, but can still get into problems. Here're -the manual steps in case it fails: - -```sh -# cd into emission directory -git pull -yarn install # include --check-files if things get weird -cd Example -bundle exec pod install # might need to add --repo-update if it fails -# Open workspace -cd .. -yarn start -``` - -## Why React Native? - -Artsy uses React Native because it -[allows Artsy engineers working on the iOS and web front-ends to share the same idioms and tooling](https://artsy.github.io/blog/2019/03/17/three-years-of-react-native/). -The primary benefit of using React Native is that -[we get to use React](https://ashfurrow.com/blog/the-case-for-react-native/#react-itself-is-amazing), which is not -only a better way of building iOS apps, but gives us those shared idioms and tools. (We're hoping to leverage React -Native for Android in the longterm, but for now that's the motivation.) - -You can think of React Native as React, but instead of targeting `div`'s and the HTML DOM, you're targeting native -views to whatever target platform you're running on. So, Artsy uses React Native to target the iOS `UIView` -hierarchy. Developers write very similar, often identical components to the web, but at runtime, they get rendered -in native iOS views instead of DOM elements. We'll take a look at `EntityHeader` as an example shortly. All the -familiar [React component lifecycle methods](https://reactjs.org/docs/react-component.html) exist in React Native -components, too. From a high-level, rendering is the same on both platforms. - -Okay, so using React Native lets us use React in a mobile context. There are a couple of problems with this: - -- **React was built for the browser**. The idiom works on mobile, but the browser constraints that have guided - React's development are often not present in mobile contexts. For example, React takes great pains to minimize - component updates because browser reflows are expensive, but reflows don't even exist in iOS apps and view - component updates are relatively cheap. -- **Mobile still has its own constraints**. React was built for the browser, but React Native has to operate on - mobile devices which have their own constraints. This leads to patterns and components that are mobile-specific - (and sometimes iOS-specific). For example, `FlatList`. - -Let's talk about `FlatList`. Consider you are Apple or Google and you're building an SDK for a smartphone in 2007. -All your resources are constrained: CPU, disk I/O, and RAM are all way slower than desktop computers _and_ storage -is limited. You decide that your mobile operating system won't support virtual memory (ie: swap space) because it's -too expensive. Also, there's no garbage collection (on iOS) because it, too, is too expensive. To support the best -user experience, you would design your app SDK to minimize memory allocations. - -Okay, keep that in mind while we think about a hypothetical app. It lists all your favourite art, and you have -thousands of favourites. As the user scrolls through a list of thousands of artworks, many artworks pass by, but -only a few are ever visible at the same time. To minimize memory allocations, mobile devices actually _reuse_ views -from the list. An artwork view that is scrolled offscreen is removed from the view hierarchy and queued up for -re-use, where it gets re-configured with new data. - -**Note**: While `FlatList` often renders homogenous data (ie: every cell is display the same kind of information), -Artsy uses it generally to render lists of content. For example, -[the Artwork component](https://github.com/artsy/emission/blob/16cedc92d3420b6b94d78d7f38b9bbc71f152c09/src/lib/Scenes/Artwork/Artwork.tsx#L206-L219) -is one large list. - -(This pattern is called _list virtualization_ and can be applied in many different contexts, mostly for performance -reasons. Check out [this library](https://github.com/bvaughn/react-virtualized) for a general-purpose React -implementation.) - -Although the resource constraints of 2007 aren't as relevant in 2019, this pattern persists across iOS and Android -because it's _really_ performant, and there is now more than a decade of APIs built atop it. It is with this spirit -that React Native introduced the `FlatList` component. - -All this dequeuing business takes a lot of code, and `FlatList` handles it all for you. Let's compare what a list -in React looks like compared to React Native. - -```tsx -// In React -{conversations.map(({item}, index) => ( // Here's the data. - -)} - -// In React Native - String(index)} // Here's what keys to use. - renderItem={({ item }) => ( // Here's how to render each data item. - )} -/> -``` - -## Shared Infrastructure - -In 2015, web and mobile engineers at Artsy used the following tools in common: - -- GitHub. - -The teams were operating in silos, and React/Native have really brought us together. Here are some of the tools -that we now share in common: - -- [React](https://reactjs.org) (of course). -- [Relay](https://relay.dev) (and Metaphysics!). -- [Styled-system](https://github.com/styled-system/styled-system) / [Palette](https://github.com/artsy/palette). -- [TypeScript](https://www.typescriptlang.org). -- [Jest](https://jestjs.io). -- Tonnes of node modules (lodash, react-tracking, etc). -- [VSCode](https://code.visualstudio.com). -- ... and GitHub. - -So we've gone from a point where two teams were using separate languages, text editors, testing frameworks, network -libraries... to a point where it's no longer productive to think about "web" and "mobile" engineers. Pretty great! - -Let's dive into detail on Palette because, for front-end work, it's one of the most important packages to -understand. If you're using to using Palette on the web, then you'll _mostly_ be able to use Palette in React -Native like you're used to. We're going to look at two examples, of a fundamental component and a -slightly-more-complex component. - -First, `Box`. Developers who have used Palette are accustomed to writing components with `Box` rather than `div`. -Why is that? Well, there are a few reasons, but one of the most important ones is to make the component compatible -with React Native. Earlier, we said: - -> You can think of React Native as React, but instead of targeting `div`'s and the HTML DOM, you're targeting -> native views to whatever target platform you're running on. - -`div` is an HTML-specific component, and `Box` is designed to be cross-platform. Let's see what it looks like in -the Palette source code. -[`Box` is defined as a "primitive view"](https://github.com/artsy/palette/blob/cef079d8a81002533fc9a977d9ba717a9eafd835/packages/palette/src/elements/Box/Box.tsx#L59): - -```tsx -export const Box = primitives.View` -``` - -`primitives` come from Palette, which is defined in two files. - -```sh -➜ tree packages/palette/src/platform/ -packages/palette/src/platform/ -β”œβ”€β”€ ... -β”œβ”€β”€ primitives.ios.ts -└── primitives.ts -``` - -React Native will import the `.ios.ts` file when it gets `import`ed from iOS. Similarly, it will import -`.android.ts` (but we don't have an Android app). The web defaults to just `.ts`. -[Check out the docs](http://facebook.github.io/react-native/docs/platform-specific-code) for more detail on this -module resolution. - -So when the `Box` is defined as `styled.View`, it's being defined -[as a styled-components `div` on web](https://github.com/artsy/palette/blob/cef079d8a81002533fc9a977d9ba717a9eafd835/packages/palette/src/platform/primitives.ts#L5-L9) -and -[as a styled-components `View` on iOS](https://github.com/artsy/palette/blob/cef079d8a81002533fc9a977d9ba717a9eafd835/packages/palette/src/platform/primitives.ios.ts#L7-L11). -(Styled-components is doing a bit more abstraction for us here, depending on iOS-vs-Android. Ultimately, on iOS, -`Box` components are rendered as native Objective-C `UIView` subclasses.) - -Let's take a look at a more complex component example. - -![Comparison between Reaction and Emission](./images/session-two-comparison.png) - -Oh so that's interesting! Both Reaction and Emission are rendering an `EntityHeader` for the same entity, but they -look different! Why is that? - -Palette sometimes makes idiom-specific decisions about how a component should be rendered on web or iOS. In this -case, we've turned the "Follow" link into a "Follow" button. - -> **Q**: Why do you think a button would be better than a link in this case? -> -> **A**: Because it will be a larger tap target for users trying to hit the button with their fingers (instead of a -> mouse cursor). - -Okay so if Reaction and Emission use the same component, how does this work? Let's look at -[Palette's code](https://github.com/artsy/palette/tree/master/packages/palette/src/elements/EntityHeader). - -```sh -➜ tree packages/palette/src/elements/EntityHeader -packages/palette/src/elements/EntityHeader -β”œβ”€β”€ EntityHeader.ios.tsx # iOS-specific implementation. -β”œβ”€β”€ EntityHeader.tsx # Web-specific (ie: default) implementation. -└── index.tsx # The magic that makes it work. -``` - -`index.tsx` is how this component gets imported by both Reaction and Emission, and this is where the magic happens: - -```sh -➜ cat packages/palette/src/elements/EntityHeader/index.tsx -export * from "./EntityHeader" -``` - -The trick is that we have Palette's module lookup configured to import `EntityHeader.tsx` on web, and -`EntityHeader.ios.tsx` on iOS, when _this_ file imports `from "./EntityHeader"`. So the resolution happens within -Palette, abstracted from the developer using the component. - -The big exception to our shared infrastructure is displaying images in React Native. Emission has its own -`OpaqueImage` component that you should use instead – it shares an image cache with our native Objective-C / Swift -code. - -> **Q**: If React Native uses CSS, what units does it support? -> -> **A**: React Native, like iOS, supports a kind of device-independent virtual pixel size called _points_. On -> 1x-scale devices (like the original iPhone) 1pt = 1px. On a 2x-scale device (like an iPhone 8) 1pt = 2px. We lay -> out our UI in points to avoid having to deal with these differences. -> [Check out this blog post](https://medium.com/@0saurabhgour/react-native-density-independent-pixels-pixelratio-1f10d86f631) -> or [the official docs](https://facebook.github.io/react-native/docs/pixelratio) for more information. Typically, -> though, we use styled-system and Flexbox (via [Yoga](https://yogalayout.com)) to avoid having to specify these -> values manually. - -## iOS-Specific Infrastructure - -Everything the user sees in the app can be categorized into one of the following: - -- Native code (aka "UIKit code", Objective-C and Swift in Eigen) -- React Native (TypeScript in Emission) -- Web view (could be from anywhere!) - -Web views are really interesting. In some ways, they've helped us move fast without maintaining strict parity -between web and iOS. On the other hand, we've also got some views that _should_ be native views, but are still web -views. Let's contrast some examples: - -
- -**Q**: The BNMO flow is shown in a web view. What are the advantages/disadvantages of this approach? - - - -The BNMO flow is changed frequently, for example when we added SCA compliance. By building the flow in a web view, -we not only get to re-use the web implementation on iOS, but we also get users to see the latest-deployed web -version at any time. This bypasses App Store review, which is really convenient. - -
- -
- -**Q**: The Partner page (eg: a gallery) is shown in a web view. It looks awful, because the mobile web partner page -looks bad. But this could be an advantage, too! Why is that? - - - -**A**: The advantage of having the Partner page be a web view is that, if we improve the web view, iOS users see -the improved version without having to update their app version. - -
- -
- -**Q**: The BidFlow project, built by Purchase in 2018, is _not_ a web view. It's built in React Native. Why do you -think that was? - - - -**A**: BidFlows on web and iOS differ because user expectations on those platforms differ. By building the BidFlow -in iOS, we adhere more closely to the users' expectations during this critical path. It also gives us a "native -feel" to the app, by using UI controls that aren't available on the web (like `UIPickerView` for selecting a bid -increment). - -
- -(Routing between views in our app always happens via URLs in Eigen, in the ARSwitchboard class (DEPRECATED). If there is no route -for a given URL, it falls back to a web view. You can -[check out the docs for adding new components](https://github.com/artsy/emission/blob/master/docs/adding_new_components.md) -for more information about how Eigen/Emission routing interop works.) - -Okay that's all great, but how do we know where the code lives? If you have a bug report, how do you know where to -look for the code you'll need to change to fix the bug? -[We have documented instructions](../finding-code.md) for this. The process involves finding the _view -controller_ for a given user interface, and then working backwards from there. We'll learn about view controllers -next week. - -## Let's see some code - -Objective-C predates what we'll call "common" programming syntax. The following code syntax is representative of -JavaScript, C, C++, Ruby, and a bunch of other languages: - -```ts -this.relay.refetch(params, null) -``` - -So what would this same idea look like, in Objective-C? - -```objc -[self.relay refetch:params renderVariables:nil]; -``` - -Whoa! Really different. Objective-C has dot-syntax for property access, so `self.relay` works as you'd expect. But -we have to prefix the object that we're calling the function on with `[`. `params` look similar to the first code -example, but what is `renderVariables`? Well, Objective-C includes named parameters _at the call site_. This can -get a little confusing, but we'll cover Objective-C in more detail next week. - -And what about Swift? Well, Swift is kind of a weird mix of both: - -```swift -self.relay.refetch(params, renderVariables: nil) -``` - -We're going to focus mainly on Objective-C for this course, since -[Objective-C is our preferred native language](https://github.com/artsy/README/pull/217). - -## Core Concept Review & Homework - -- What are some of the constraints on mobile devices that aren't present on web browsers? What about mobile - browsers, did they also have these constraints? Do they still have them? -- We've seen how Palette components differ across iOS and web, like the `EntityHeader`. How do you think this - affects the Relay fragments we use for this component? -- Recall the idea you came up with for the app, from last week's homework. Considering that features in the app are - built in one of three places (native code, React Native code, or a web view), where do you think Artsy should put - build your idea? Why? -- Extra credit: we briefly looked at how to call a function in Objective-C. How do you think that function is - defined at the _implementation_ site? - -## Resources / Recommended Reading - -- [Artsy's 3-year retrospective on React Native](https://artsy.github.io/blog/2019/03/17/three-years-of-react-native/) -- [Platform-specific code in React Native](http://facebook.github.io/react-native/docs/platform-specific-code) -- [How to find code for UI in the app](https://github.com/artsy/README/blob/master/resources/mobile/finding-code.md) -- [The Objective-C Language Reference](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html) -- [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) diff --git a/resources/mobile/release-cadence.md b/resources/mobile/release-cadence.md deleted file mode 100644 index 24a6cf25..00000000 --- a/resources/mobile/release-cadence.md +++ /dev/null @@ -1,50 +0,0 @@ -# 2-week Release Cadence - -After researching -[other companies' practices](https://www.notion.so/artsy/Investigate-Industry-Use-of-App-Release-Cadences-5a32fc82550841b984eacc96db46d9ee) -πŸ”, we're going to implement a 2-week app release cadence. Our app gets the most use on the weekends, so we want to -make sure any new features are released (and monitored) ahead of the weekend. - -## Principles - -- All product work is kept behind a Lab Option ([example PR](https://github.com/artsy/eigen/pull/2934)) -- When ready to be deployed, risky features should be put behind Echo flags - ([example PR](https://github.com/artsy/eigen/pull/2937)) - -## Cadence - -Below is a hypothetical release calendar showing how sprints align with App Store submissions and release. - -Let's consider a 10-day sprint (2 weeks = 10 business days). We would commit to the following: - -- **Sprint Start +0days** β†’ Start the sprint - - Use next 5 days to complete any work we want to release this sprint. -- **Sprint Start +5days** β†’ Test our work in Sprintly QA Session - - We have that afternoon to fix bugs, or put disable the Echo flag for broken features - - To keep teams unblocked, any changes should go into a separate branch, which we can merge into master after - being released. -- **Sprint Start +5 days** β†’ Submit to App Store review (pending our release) - - Once we get approval, we can choose when to release to users. - - Approval typically takes a day. -- **Sprint Start +7days** β†’ Release to users. Monitor. - - We would wait until Wednesday to release and monitor. This gives us two business days to submit any emergency - hot fixes, but ideally, we could just disable the Echo flag for catastrophic bugs. - -In effect, this splits our sprint work into two sections: work we expect to release this sprint, and work that we -plan on _doing_ this sprint but won't get released until next sprint. Submitting halfway through a sprint, and -releasing at the top of the next sprint, does introduce some complexity. However, we can socialize the schedule and -get everyone on-board. - -The alternative would be to release at the start/end of a sprint, but since App Store reviews and releases take -such a long time, we would rather have our cadence embrace this ambiguity by submitting mid-sprint. - -![Calendar](./images/release-calendar.png) - -## Monitoring - -It's important to note that beyond **Sentry** monitoring, which can only report crashes once the app has successful -launched a subsequent session, we need to also **monitor Apple crash reports**. These reports are opt-in, so we'll -see fewer of them, but are done at an OS-level that doesn't require the app to successfully launch. This would help -us catch crashing bugs that prevent the app from launching at all, such as Incident 61 -([see postmortem](https://artsy.app.opsgenie.com/reports/post-mortem/056fd5f9-66e4-4e96-81e7-4f39f9884b9c/detail) -πŸ”). diff --git a/resources/mobile/summary.json b/resources/mobile/summary.json deleted file mode 100644 index cac70734..00000000 --- a/resources/mobile/summary.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Artsy Mobile Learning Resources", - "description": "Collections of further reading about iOS and, more specifically, iOS at Artsy." -} diff --git a/resources/mobile/xcode.md b/resources/mobile/xcode.md deleted file mode 100644 index e0d67070..00000000 --- a/resources/mobile/xcode.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: How to Use Xcode -description: Introduction and further resources to using Xcode ---- - -## Installing Xcode - -Xcode is Apple's IDE for developing, among many other things, iOS applications. You can install Xcode in one of two -ways: - -- [From the App Store](https://apps.apple.com/us/app/xcode/id497799835?mt=12) -- [From Apple's Developer portal πŸ”](https://developer.apple.com/download/more/) - -Either works, **but beware**! If you have App Store automatic updates enabled, you may come in to work one day and -find that your IDE has changed (which has implications for Swift language and SDK versions). We recommend -downloading Xcode manually from [Apple's Developer portal πŸ”](https://developer.apple.com/download/more/). - -You can have two different versions of Xcode installed on your computer, which is useful when we haven't yet -upgraded old projects to be compatible with the latest Xcode version. They are just a Mac app like any other, so we -can organize them in the `/Applications` directory. Here's a common setup: - -``` -/Applications -β”œβ”€β”€ Xcode9/Xcode.app -β”œβ”€β”€ Xcode10/Xcode.app -└── Xcode11/Xcode.app -``` - -Note that none of the paths have spaces in them. Xcode gets invoked one of two ways, and switching which version is -invoked is different for each one: - -- Opened from Finder, Xcode projects+workspaces can change versions by right-clicking and selecting "Open with...", - optionally using "Get Info" to make the change permanent. -- Compiled from the command like, use `xcode-select`. `-p` will print the path to the current Xcode, and `-s` will - switch it. For example: `sudo xcode-select -s /Applications/Xcode10/Xcode.app`. - -What about simulators? Officially, you need to run the new Xcode versions to use the new iOS version in a -simulator. -[Unofficially, there is a workaround](https://gist.github.com/steipete/d9b44d8e9f341e81414e86d7ff8fb62d). The tl;dr -is to copy a directory from the new Xcode version to the old Xcode app's bundle. With the above directory structure -as an example, one could install the iOS 13 simulators from Xcode 11 into Xcode 10 with the following commands: - -```sh -cp -r /Applications/Xcode11/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13* /Applications/Xcode10/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport -``` - -You'll need to restart Xcode. - -## Getting used to Xcode - -Xcode is really different from VSCode. It's Apple's opinion on what a good IDE should be, and sometimes it's -uncomfortable at first. Some quick differences: - -- Xcode doesn't encourage you to have multiple files open at a time, in tabs. -- Xcode uses web browser-like history for navigating back-and-forth between files (look at the top-right corner of - the file editor for the arrows). -- Xcode's file browser doesn't represent the file system's directory structure – it is a distinct organization that - just _happens_ to match the disk's directory structure most of the time. - - diff --git a/resources/tech-learning.md b/resources/tech-learning.md index dcb5f337..1fefa317 100644 --- a/resources/tech-learning.md +++ b/resources/tech-learning.md @@ -31,12 +31,11 @@ answer to the classic interview question, "what happens when you type google.com ### JavaScript + NodeJS -_See also [Frontend Practice Resources](#frontend-practice) below_ +_See also [Frontend Practice Resources](#web-practice) below_ | Name | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | | [JavaScript: The Weird Parts][js_weird] | [video] - 'An advanced Javascript course for everyone, giving a deep understanding of the language by understanding how it works under the hood' | -| [Intro to ES6][es6] | An intro to modern JS features | | [Modern JS explained for dinosaurs][dinos] | How we got from `