Giter Site home page Giter Site logo

heckj / swiftui-notes Goto Github PK

View Code? Open in Web Editor NEW
1.9K 40.0 199.0 21.43 MB

content for Using Combine - notes on learning Combine with UIKit and SwiftUI

Home Page: https://heckj.github.io/swiftui-notes/

License: MIT License

Swift 99.46% Shell 0.54%
swiftui combine combine-framework

swiftui-notes's Introduction

Using Combine

SwiftUI-Notes Build Status

A collection of notes, project pieces, playgrounds and ideas on learning and using SwiftUI and Combine. Changes, corrections, and feedback all welcome! See CONTRIBUTING for details and links.

Goal

While I started digging into SwiftUI, I was attracted to Combine and realized how much depth there was in just the Combine framework. I wanted to learn Combine, and describing how to use it to other people works really well for me.

My goal for this is to create easily accessible reference documentation for myself, and for anyone else wanting to use it. I do (not-so-secretly) hope that Apple's own reference documentation will completely obviate the reference section of all of this, but it doesn't today.

What makes good reference documentation for me:

  • core concepts in some detail, explaining what's intended and expected
  • reference of all of the classes and functions you might use, organized and grouped by why you might be using them
    • having commented sample code that illustrate how the function or class can be used
  • common or frequent recipes/patterns of how you might want to use this framework
  • how to test/validate your own creations using the framework

Bonus points:

  • internal self-references to functions, classes, and concepts to supporting navigating based on what you're trying to learn or understand
    • self-consistent navigation of rendered content
  • usable from mobile & desktop
  • diagrams for the functions to make what they do more easily understood (aka marble diagrams)
  • consumable in multiple formats: hosted HTML, pdf, epub
    • hosted HTML easily referencable from source code

Where stuff resides

The docs directory in this repository is the source for the HTML content hosted at https://heckj.github.io/swiftui-notes/

The project (SwiftUI-Notes.xcodeproj) has sample code, tests, and trials used in building and vetting the content.

The content is hosted by Github (on github pages), generated with Jekyll, primarily written in Markdown.

Setting up asciidoctor for local rendering

  • get a more recent ruby (I'm using rbenv with brew install rbenv), current 2.6.3

The git metadata requires "rugged", which wants cmake to install it... so you might need to brew install cmake to make this all work.

rbenv install 2.6.3
rbenv global 2.6.3

gem install bundler
gem install asciidoctor
NOKOGIRI_USE_SYSTEM_LIBRARIES=1 gem install asciidoctor-epub3 --pre
gem install pygments.rb

gem install rugged # required for the git-metadata extension, requires 'cmake'

If you have docker installed, you can also use a docker image to do the rendering, and not have to install anything directly. If you want to try out different extensions, you probably want to install this locally, but if you're just generating the output then the docker path is significantly easier.

The "official" image is asciidoctor/docker-asciidoctor. I have a small variant at heckj/docker-asciidoctor that is built to include the gem rugged which is providing the git metadata resolution.

Rendering - using locally installed asciidoctor & tooling

cd docs

asciidoctor-epub3 -v -t -D output \
  using-combine-book.adoc

asciidoctor-pdf -v -t -D output \
  using-combine-book.adoc

asciidoctor -v -t -D output \
  -r ./lib/google-analytics-docinfoprocessor.rb \
  using-combine-book.adoc

Rendering - using a docker-based toolchain

You can do all this rendering locally with docker. Do this from the top of the repository:

# get the docker image loaded locally
docker pull heckj/docker-asciidoctor

# render the HTML, results will appear in `output` directory
docker run --rm -v $(pwd):/documents/ --name asciidoc-to-html heckj/docker-asciidoctor asciidoctor -v -t -D /documents/output -r ./docs/lib/google-analytics-docinfoprocessor.rb docs/using-combine-book.adoc

# render a PDF, results will appear in `output` directory
docker run --rm -v $(pwd):/documents/ --name asciidoc-to-pdf heckj/docker-asciidoctor asciidoctor-pdf -v -t -D /documents/output docs/using-combine-book.adoc

# render an epub3 file, will should appear in `output` directory
docker run --rm -v $(pwd):/documents/ --name asciidoc-to-epub3 heckj/docker-asciidoctor asciidoctor-epub3 -v -t -D /documents/output docs/using-combine-book.adoc

# copy in the images for the HTML
cp -r docs/images output/images

A variation of these commands are included in the .travisCI build configuration.

Link Validation

There's an NPM package that will hit a page and do a scan for broken links: https://www.npmjs.com/package/broken-link-checker[broken-link-checker].

To install:

npm install broken-link-checker

To run it against the live site:

./node_modules/.bin/blc http://heckj.github.io/swiftui-notes/ | grep BROKEN

Command-line build and test

xcodebuild test -scheme SwiftUI-Notes -allowProvisioningUpdates

swiftui-notes's People

Contributors

adamhaafiz avatar dave256 avatar devepre avatar drewvolz avatar exzackly avatar gatamar avatar heckj avatar huang-libo avatar ivantse avatar iwillau avatar john-mueller avatar leeomara avatar malhal avatar martinp7r avatar matteinn avatar mattmassicotte avatar maxdesiatov avatar mcritz avatar mimorocks avatar nanujogi avatar p00ya avatar palaniraja avatar paulwoodiii avatar robo-fish avatar slk333 avatar spig avatar timothyekl avatar yeland avatar zhiying-fan avatar zntfdr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftui-notes's Issues

implementing your own subscriber

I started to add a section on Implementing your own subscriber, but realized there's a lot more learning that I need to do before I can knock it out.

I'm going to kick that back in priority to getting other elements done first.

Here's the snippet that I was thinking to start with, in developingwith.adoc:

== Implementing your own Subscriber

Developing with combine can frequently leverage the two common existing subscribers <<reference.adoc#reference-assign>> and <<reference.adoc#reference-sink>>, or you are working with an Apple framework (such as https://developer.apple.com/documentation/swiftui[SwiftUI]) that is the the subscriber to which you are integrating.

In some cases, you may want to create your own subscriber.
While <<reference.adoc#reference-sink>> can handle most situations, if you want to control the demand that drives the pipelines, you need to provide your own implementation.

__YET TO BE DEVELOPED__

https://developer.apple.com/documentation/combine/subscriber[`Subscriber protocol`]

types:
* Input
* Failure

protocol required functions:
* `receive(Self.Input) -> Subscribers.Demand`
* `receive(subscription: Subscription)`
* `receive(completion: Subscribers.Completion<Self.Failure>)`

* receive() -> Subscribers.Demand (when input void)

maybe based on https://developer.apple.com/documentation/combine/anysubscriber[`AnySubscriber`] which can be initialized with 3 closures:

* receiveSubscription: `((Subscription) -> Void)?``
* receiveValue: `((Input) -> Subscribers.Demand)?``
* receiveCompletion: `((Subscribers.Completion<Failure>) -> Void)?)``

Throttle linked twice.

In the "Pipelines and threads" section you have "Examples include delay, throttle, and throttle, and these also" where throttle is linked twice. I'm not sure if you meant to link to a third thing or if it should simply be "delay and throttle".

add section on multicast (ref at least, maybe pattern as well)

multicast looks like it has a way of dropping into a passthrough subject at the end of a chain, and then you can hang multiple subscribers from that subject.

or using a currentValueSubject?

comments from swift forums made it sound like multicast was meant to be used with the shared() function to build up a class representation of a publisher or operator. i didn’t quite catch that detail though.

forum thread ref: https://forums.swift.org/t/combine-what-are-those-multicast-functions-for/26677

  • multicast

Tiny change: Change instances of "strong" to "static"

This is a tiny issue I have with the introduction (in docs/introduction.adoc) and I also am aware of how these terms are not perfectly defined et cetera, but these lines make more sense if "strong" was replaced with "static":

... applying the strongly-typed nature of Swift to their solution.

Applying these concepts to a strongly typed language like swift ...

Yes, Swift is "strongly typed", but it is also "statically typed" and, in my opinion, it is the static part that matters here.

update reference on how subscribe() works and how it impacts queues

I filed feedback (FB6727976) asking about why a specific test case was failing, asserting it was unexpected. The feedback from the development team was helpful, although I need to really grok it to understand how to integrate the feedback in the ref docs:

Engineering has the following feedback for you:
subscribe(on:) only applies to the following functions

  • Publisher's func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure
  • Subscription's func request(_ demand: Subscribers.Demand)
  • func cancel()

If you want to effect the queue affinity of downstream events such as values etc you should use receive(on:) instead (which is perhaps the more common of the two, usages of subscribe(on:) should be far and few between)

add some sort of link checker to the generated output

It'd be sufficient to run a validation against the generated HTML (since the PDF and ePub use the same links), but some of the links I started with in beta2 have definitely gone out of date, and I'm not entirely sure I've caught them all.

I could really use something to read through the whole HTML page, gather up all the links and then validate that they work - but especially for the external links, as internal references I at least get warnings from AsciiDoctor.

update the networkRequest patterns

  • flesh out examples
  • make tests

add details to references:

  • <<reference.adoc#reference-tryMap>>
  • <<reference.adoc#reference-map>>
  • <<reference.adoc#reference-decode>>
  • <<reference.adoc#reference-encode>>

update example to use beta4 cancellable.store

beta4 added a method “store” to cancellable to promote collecting all the cancellable elements together, which cancel on dealloc

update an example (or a couple) to show this in use

Typo Error in Combine notes

** original text **
To illustrate changing types within a pipeline, here is a short snippet thsat starts with a publisher that generates , and end with a subscription taking , .

**correct text **
To illustrate changing types within a pipeline, here is a short snippet that starts with a publisher that generates , and end with a subscription taking , .

typo-error1

2nd.

original text
The interals of Combine are all driven by the subscriber. This is how Combine supports the concept of back pressure.

correct text
The internals of Combine are all driven by the subscriber. This is how Combine supports the concept of back pressure.

typo-error2

consistency/format in tests

iterate through tests referenced from the references.adoc

  • verify test name matches what the test is doing
  • add explanatory comments of what the test is doing
  • comment around XCTFail for what it's doing
  • add patterns-testing references to tests to explain what/how they're working

Update pattern 7

multiple element synchronization

  • 7.1) sequence of async operations w/ Future
  • 7.2) parallel operations, bringing together with zip

coreconcept diagram - pub/sub

Existing ascii art:

Publisher source       Subscriber
+--------------+      +--------------+
|        <Output> --> <Input>        |
|       <Failure> --> <Failure>      |
+--------------+      +--------------+

in the content/context at https://heckj.github.io/swiftui-notes/#core-publisher-subscriber

key feature:

  • showing data flow of separate type structures for output -> Input and failure -> failure

wasn't attempting to show the subscribe/control flow, more of the types that an operator can express

note that this is intermediate to advanced topic

i received an email that in the end was useful, but started out as “you’re a terrible writer”. My takeaway was that the individual thought the book was going to be a gentle/beginners introduction- when it pretty much assumes a ton of knowledge and dives into the deep end quickly.

it could really use some preface work warning about this - likely in the gumroad “about this book” too - so that people can see and know what to expect.

sequential operators

  • first
  • firstWhere
  • tryFirstWhere
  • last
  • lastWhere
  • tryLastWhere
  • dropUntilOutput
  • dropWhile
  • tryDropWhile
  • concatenate
  • drop
  • prefixUntilOutput
  • prefixWhile
  • tryPrefixWhile
  • output

update Assign - error with optional type assignment

I reported the strange error as feedback, but also asked in the dev forms, why I wasn't able to assign() to image on UIImageView.

The answer was that the error was a type mismatch. It was attempting to assign UIImage to UIImage? (an optional), which caused the compiler to have it's freakout, reporting only: error: type of expression is ambiguous without more context

One option that was suggested was making a non-optional argument, and assigning into that.

In any case, it's good detail for updating the reference on assign() and how to use it

Shouldn't you use an optional instead of empty array?

In the Cascading UI updates including a network request section, you mention making an array even though you expect one value so that you can have an empty one when there is no value. Why wouldn't you use an optional in this case and then compactMap instead? If there is a reason, then I would mention it here.

This is a fantastic resource btw. Thank you very much!

I see a blank screen.

I attempted to run your application; but only got:

2019-11-17 11:00:28.783593-0800 SwiftUI-Notes[12220:411888] Metal API Validation Enabled

pattern 8 & combineLatest

drop pattern 8

  • add a pattern (earlier, with UIKit pieces? - pattern 5.3 example?) focused on watching multiple form inputs and activating a submit button with "combineLatest"

fan-in/merge vs. fan-out of pattern 5.2

consistency check in reference

  • format all operator names with ` marks.
  • get rid of period and () in .operator() references outside of code blocks
  • consider search & replace for operators to always point to reference block
  • review all code for reference/description details and any needed explanation

ASCII art should not wrap

On small screens, the pipelines representation are not legible. I think these snippets should be made horizontally scrollable and not wrap.

85A8D874-3151-421D-B989-DF75AAA80F97

add UIKit example pattern

  • @published propertyWrapper
  • flesh out code, make into solid examples and explain code

refs:

  • published
  • empty
  • published
  • switchToLatest
  • filter
  • handleEvents
  • throttle
  • debounce
  • removeDuplicates
  • tryRemoveDuplicates

How to use combine for text entered into a SwiftUI Textfield?

Hi,

first: Great document. The most complete and understandable text explaining the combine framework! Keep up the good work!

In one of the WWDC session (https://developer.apple.com/videos/play/wwdc2019/712/ starting at 11m 50s) the presenter explains how to process the stream of text coming from a TextField.
Unfortunately the presentation does this all on slides, there is never any concrete code or a demo.

For your "Patterns" section: How would you do this using a TextField from SwiftUI?

I found some examples using PassthroughSubject, but ideally there should be an easier way to subscribe to text changes from a TextField.

perhaps make a new section (before?) patterns - using Combine with APIs

It's not quite "core concepts", and it's not patterns or reference. As I've been doing examples with Combine, it's clear there's a number of techniques that will probably emerge on how to use Combine from within your API, or how to provide combine publishers for consumption.

  • how to bring in data to an operator and get something else back out
  • how to provide an API that can be used in coordination with other combine pipelines
    (it'd be nice if there was a subscriber you make - can't make your own custom operators right now to do arbitrary updates and return new values down the pipe...)
    today, you can:
    .map {} and create a publisher (or pipeline), followed by switchToLatest
    .flatMap {} and create a publisher within that closure
    write to a @published property, and then hang multiple subscribers from that endpoint

highlight API example that I did showing multiple publishers - you can get the data itself, and then there's often metadata (network activity) that you're interested in knowing about as well.
It certainly feels like a reasonable pattern to set up (and reason about) is a configuration for a publisher that you can tweak, and then feeds about that publisher

  • the data itself that it's returning
  • if it's "active" - (CoreLocation data can be toggled on/off for example)
  • more complex status (current configuration, authentication status and permission scopes returned, API response details, metrics about usage?)

It might also be a good place to talk about converting object/delegate patterns of some API endpoints (CoreLocation, for example) and how to convert those into publisher data sources

add testing examples

  • might be fleshing out from earlier examples
  • add explanation text for setting up patterns
  • rough into actual tests as well

UIKit-Combine Black Screen while running

UIKit-Combine while Launching in iOS simulator shows Black Screen.
Solution: Create an new project copy paste ViewController.swift & Main.storyboard.
Compile & run in iOS simulator it works. Image attached for reference only.
Black-Screen

add debugging patterns

  • update reference for print
  • update reference for handleEvents
  • update reference for breakpoint
  • create testing code showing breakpoint working
    • break out subpattern for print,
    • handleevent (or maybe map),
    • and breakpoint
  • breakpoint should also reference setting a breakpoint in an operator's closure

Typo in Combine Specifics

** original text **

Combine specifics
... Combine embeds the concept of back-pressure, which allows the subscriber to control how much information is gets at once and needs to process.

** suggested text **

replace "is" with "it":

... Combine embeds the concept of back-pressure, which allows the subscriber to control how much information it gets at once and needs to process.

Completion-aware publishers

Hi Joseph,

I was wondering if there are "Completion-aware" operators in Combine, such as the ReactiveSwift or RxSwift then operator, which lets you start a new publisher once the upstream have finished successfully (in case of failure, the error will be forwarded downstream). Any ideas?

Combine do have allSatisfy which can be tweaked for that purpose, but I guess it will be better to have a specific Publisher and I don't know how to create custom Publishers yet :)

Keep up the great work!

add Future pattern

  • example with Vision framework and camera data?
  • link to references, extend on Promise/Future
  • add tests
  • set up example in code project

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.