mirror of https://github.com/lukechilds/docs.git
Thomas Osmonson
4 years ago
110 changed files with 1070 additions and 1963 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,102 @@ |
|||
sections: |
|||
- pages: |
|||
- path: / |
|||
- path: /build-an-app # is an overview page |
|||
- path: /smart-contracts |
|||
pages: |
|||
- path: /overview |
|||
- path: /testing-contracts |
|||
- path: /principals |
|||
- path: /running-testnet-node |
|||
sections: |
|||
- title: Tutorials |
|||
pages: |
|||
- path: /hello-world-tutorial |
|||
- path: /counter-tutorial |
|||
- path: /signing-transactions |
|||
- path: /mining # is an overview page |
|||
|
|||
- title: Technology |
|||
pages: |
|||
- path: /authentication |
|||
pages: |
|||
- path: /overview |
|||
- path: /profiles |
|||
- path: /connect |
|||
sections: |
|||
- title: Tutorials |
|||
pages: |
|||
- path: /building-todo-app |
|||
- path: /data-storage |
|||
pages: |
|||
- path: /overview |
|||
- path: /storage-guide |
|||
- path: /authentication |
|||
- path: /storage-write-read |
|||
- path: /collections |
|||
- path: /collection-type |
|||
- path: /data-indexing |
|||
pages: |
|||
- path: /overview |
|||
- path: /integrate |
|||
- path: /models |
|||
- path: /collaborate |
|||
- path: /server-extras |
|||
- path: /stacks-blockchain |
|||
pages: |
|||
- path: /overview |
|||
- path: /best-practices |
|||
- path: /wire-format |
|||
- path: /atlas |
|||
pages: |
|||
- path: /overview |
|||
- path: /how-atlas-works |
|||
- path: /usage |
|||
sections: |
|||
- title: Tutorials |
|||
pages: |
|||
- path: /install-api |
|||
- path: /installing-memcached |
|||
- path: /naming-services |
|||
pages: |
|||
- path: /overview |
|||
- path: /architecture |
|||
- path: /comparison |
|||
- path: /did |
|||
- path: /forks |
|||
- path: /namespaces |
|||
- path: /subdomains |
|||
- path: /forks |
|||
sections: |
|||
- title: Tutorials |
|||
pages: |
|||
- path: /build-profile-search-index |
|||
- path: /choose-name |
|||
- path: /create-namespace |
|||
- path: /resolve-name |
|||
- path: /register-name |
|||
- path: /manage-names |
|||
- path: /subdomains-tutorial |
|||
- path: /storage-hubs # this seems out of date, kill? |
|||
pages: |
|||
- path: /overview |
|||
- path: /hello-hub-choice |
|||
- path: /gaia-admin |
|||
- path: /config-schema |
|||
- path: /digital-ocean-deploy |
|||
- path: /amazon-s3-deploy |
|||
- title: Ecosystem |
|||
pages: |
|||
- path: /overview #pbc et al |
|||
- path: /stacks-token |
|||
- path: /contributing |
|||
|
|||
- title: References |
|||
pages: |
|||
- path: /clarity-reference |
|||
- path: /blockstack-cli-reference |
|||
- path: /stacks-blockchain-reference |
|||
- path: /stacks-rpc-api-reference |
|||
- path: /faqs |
|||
- path: /glossary |
|||
- path: /deploy-tips |
@ -1,42 +1,113 @@ |
|||
import React from 'react'; |
|||
import { Box, color, Grid, space } from '@blockstack/ui'; |
|||
import { onlyText } from '@common/utils'; |
|||
import { useAppState } from '@common/hooks/use-app-state'; |
|||
import { Text } from '@components/typography'; |
|||
import { Box, Flex, BoxProps, color, Grid, space, transition } from '@blockstack/ui'; |
|||
import { border, onlyText } from '@common/utils'; |
|||
import { useTouchable } from '@common/hooks/use-touchable'; |
|||
import { Caption, Text } from '@components/typography'; |
|||
import Link from 'next/link'; |
|||
import routes from '@common/routes'; |
|||
|
|||
const Image = (props: BoxProps) => ( |
|||
<Box |
|||
transition="all 0.75s cubic-bezier(0.23, 1, 0.32, 1)" |
|||
flexShrink={0} |
|||
bg="#9985FF" |
|||
{...props} |
|||
/> |
|||
); |
|||
|
|||
const Title = ({ children, ...props }: BoxProps) => ( |
|||
<Text fontWeight="500" display="block" {...props}> |
|||
{children} |
|||
</Text> |
|||
); |
|||
|
|||
const Description = ({ children, ...props }) => ( |
|||
<Caption maxWidth="50ch" {...props}> |
|||
{children} |
|||
</Caption> |
|||
); |
|||
|
|||
const FloatingLink = ({ href, ...props }: any) => ( |
|||
<Link href={href} passHref> |
|||
<Box as="a" position="absolute" size="100%" left={0} top={0} /> |
|||
</Link> |
|||
); |
|||
const InlineCard = ({ page }) => { |
|||
const { hover, active, bind } = useTouchable({ |
|||
behavior: 'link', |
|||
}); |
|||
return ( |
|||
<Flex |
|||
border={border()} |
|||
borderColor={hover ? '#9985FF' : color('border')} |
|||
p={space('base-loose')} |
|||
borderRadius="12px" |
|||
align="center" |
|||
transition={transition} |
|||
boxShadow={hover ? 'mid' : 'none'} |
|||
position="relative" |
|||
{...bind} |
|||
> |
|||
<Image borderRadius={hover ? '100%' : '12px'} mr={space('base')} size="64px" /> |
|||
<Flex flexDirection="column"> |
|||
<Flex align="baseline"> |
|||
<Title color={hover ? color('accent') : color('text-title')} mb={space('extra-tight')}> |
|||
{page.title || page.headings[0]} |
|||
</Title> |
|||
{page.tags?.length |
|||
? page.tags.map(tag => ( |
|||
<Flex |
|||
ml={space('tight')} |
|||
borderRadius="18px" |
|||
px={space('base-tight')} |
|||
height="20px" |
|||
align="center" |
|||
justify="center" |
|||
fontSize="12px" |
|||
bg={color('border')} |
|||
textTransform="capitalize" |
|||
color={color('invert')} |
|||
transition={transition} |
|||
> |
|||
{tag} |
|||
</Flex> |
|||
)) |
|||
: null} |
|||
</Flex> |
|||
<Description>{page.description}</Description> |
|||
</Flex> |
|||
<FloatingLink href={`${page.path}`} /> |
|||
</Flex> |
|||
); |
|||
}; |
|||
|
|||
export const PageReference = ({ children }) => { |
|||
const { routes } = useAppState(); |
|||
const content = onlyText(children).trim(); |
|||
const [variant, _paths] = content.includes('\n') ? content.split('\n') : ['default', content]; |
|||
const paths = _paths.includes(', ') ? _paths.split(', ') : [_paths]; |
|||
const flatRoutes = routes.flatMap(section => |
|||
section.routes.map(route => ({ |
|||
section: section.title, |
|||
...route, |
|||
})) |
|||
); |
|||
if (!routes) return null; |
|||
|
|||
const pages = paths |
|||
.map(path => flatRoutes.find(route => route.path === path)) |
|||
.filter(page => page); |
|||
const pages = paths.map(path => routes?.find(route => route.path === path)).filter(page => page); |
|||
return ( |
|||
<Grid |
|||
mt={space('extra-loose')} |
|||
gridColumnGap={space('loose')} |
|||
gridTemplateColumns="repeat(3, 1fr)" |
|||
gridTemplateColumns={`repeat(${pages.length === 1 ? 1 : 3}, 1fr)`} |
|||
> |
|||
{pages.map(page => ( |
|||
<Box position="relative"> |
|||
<Box mb={space('loose')} borderRadius="12px" height="144px" width="100%" bg="#9985FF" /> |
|||
<Box pb={space('tight')}> |
|||
<Text fontWeight="600">{page.title || page.headings[0]}</Text> |
|||
{pages.map(page => |
|||
variant === 'inline' ? ( |
|||
<InlineCard page={page} /> |
|||
) : ( |
|||
<Box position="relative"> |
|||
<Image height="144px" borderRadius="12px" mb={space('loose')} /> |
|||
<Flex alignItems="flex-start" justifyContent="flex-start" flexDirection="column"> |
|||
<Title mb={space('tight')}>{page.title || page.headings[0]}</Title> |
|||
<Description>{page.description}</Description> |
|||
</Flex> |
|||
<FloatingLink href={`${page.path}`} /> |
|||
</Box> |
|||
<Box>{page.description}</Box> |
|||
<Link href={`/${page.path}`} passHref> |
|||
<Box as="a" position="absolute" size="100%" left={0} top={0} /> |
|||
</Link> |
|||
</Box> |
|||
))} |
|||
) |
|||
)} |
|||
</Grid> |
|||
); |
|||
}; |
|||
|
@ -1,586 +0,0 @@ |
|||
--- |
|||
description: Learn about the Android SDK |
|||
--- |
|||
|
|||
# Android DApps |
|||
|
|||
This tutorial is written for readers who are new to either or both Blockstack |
|||
and Android to create a decentralized application. It contains the following |
|||
content: |
|||
|
|||
This tutorial was extensively tested using Android Studio 3.6 on a Dell XPS 13 |
|||
running Ubuntu 19. If your environment is different, you may encounter |
|||
slight or even major discrepancies when performing the procedures in this |
|||
tutorial. Please [join the Blockstack discord server](https://chat.blockstack.org) |
|||
and post questions or comments to the `#support` channel. |
|||
|
|||
Finally, this tutorial is written for all levels from the beginner to the most |
|||
experienced. For best results, beginners should follow the guide as written. It |
|||
is expected that the fast or furiously brilliant will skip ahead and improvise |
|||
on this material at will. Fair journey one and all. |
|||
|
|||
If you prefer, you can skip working through the tutorial all together. Instead, |
|||
you can [download the final project code](images/helloandroid.zip) and import it |
|||
into Android Studio to review it. |
|||
|
|||
## Understand the sample application flow |
|||
|
|||
When complete, the sample application is a simple `hello-world` display. It is |
|||
intended for user on an Android phone. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
Only users with an existing blockstack id can run your |
|||
final sample application. When complete, users interact with the sample |
|||
application by doing the following: |
|||
|
|||
![](images/app-flow.png) |
|||
|
|||
## Set up your environment |
|||
|
|||
This sample application has two code bases, a Blockstack `hello-blockstack` |
|||
web application and a `hello-android` Android application. Before you start |
|||
developing the sample, there are a few elements you need in your environment. |
|||
|
|||
### Install Android Studio |
|||
|
|||
If you are an experienced Android developer and already have an Android |
|||
development environment on your workstation, you can use that and skip this |
|||
step. However, you will need to adjust the remaining instructions for your |
|||
environment. |
|||
|
|||
Follow the installation instructions to download and [Android Studio |
|||
3.6](https://developer.android.com/studio/install) for your operating system. |
|||
Depending on your network connection, this can take between 15-30 minutes. |
|||
|
|||
![](images/studio-download.png) |
|||
|
|||
### Do you have Node.js? |
|||
|
|||
Node.js v10 or higher is recommended the minimum supported version is Node.js v8. Before you begin, |
|||
verify you have the correct version of Node.js and its tools installed. |
|||
|
|||
```bash |
|||
$ node -v |
|||
v12.10.0 |
|||
$ which npm npx |
|||
/usr/local/bin/npm |
|||
/usr/local/bin/npx |
|||
``` |
|||
|
|||
If you don't have these installed, take a moment to install or upgrade as needed. |
|||
|
|||
## Build the Blockstack hello-world web app |
|||
|
|||
In this section, you build a Blockstack `hello-world` web application that acts as the companion site to |
|||
your Android application. |
|||
|
|||
### Generate and launch your hello-blockstack application |
|||
|
|||
@include "scaffolding.md" |
|||
|
|||
In this section, you build an initial React.js application called |
|||
`hello-blockstack`. |
|||
|
|||
1. Create a `hello-blockstack` directory. |
|||
|
|||
```bash |
|||
mkdir hello-blockstack |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-blockstack |
|||
``` |
|||
|
|||
3. Use the Blockstack application generator to create your initial `hello-blockstack` application. |
|||
|
|||
```bash |
|||
$ npx generator-blockstack --plain |
|||
create package.json |
|||
create .gitignore |
|||
create webpack.config.js |
|||
create netlify.toml |
|||
create firebase.json |
|||
... |
|||
npm WARN ajv-errors@1.0.1 requires a peer of ajv@>=5.0.0 but none is installed. You must install peer dependencies yourself. |
|||
|
|||
added 840 packages from 582 contributors and audited 843 packages in 18.84s |
|||
found 0 vulnerabilities |
|||
``` |
|||
|
|||
Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section. |
|||
|
|||
4. Depending on your environment, respond to the prompts to populate the initial app. |
|||
You might see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. After the initial setup you can now run the initial application. |
|||
|
|||
```bash |
|||
$ npm run start |
|||
|
|||
> hello-blockstack-webpack@0.0.0 start /home/user/hello-blockstack |
|||
> webpack-dev-server |
|||
|
|||
ℹ 「wds」: Project is running at http://localhost:9000/ |
|||
ℹ 「wds」: webpack output is served from / |
|||
ℹ 「wds」: Content not from webpack is served from /home/user/hello-blockstack/dist |
|||
ℹ 「wdm」: Hash: f5d88efe9c9194f66ddd |
|||
Version: webpack 4.43.0 |
|||
Time: 2733ms |
|||
Built at: 05/19/2020 10:44:50 AM |
|||
Asset Size Chunks Chunk Names |
|||
main.js 5.39 MiB main [emitted] main |
|||
.... |
|||
[./src/index.js] 1.8 KiB {main} [built] |
|||
+ 610 hidden modules |
|||
ℹ 「wdm」: Compiled successfully. |
|||
``` |
|||
|
|||
The system opens a browser displaying your running application. |
|||
|
|||
![](images/blockstack-signin.png) |
|||
|
|||
At this point, the browser is running a Blockstack server on your local host. |
|||
This is for testing your applications only. |
|||
|
|||
6. Choose **Get started** |
|||
|
|||
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one. |
|||
|
|||
![](images/create-restore.png) |
|||
|
|||
7. Follow the prompts appropriate to your situation. |
|||
|
|||
At this point you have only a single application |
|||
on your test server. So, you should see this single application, with your |
|||
own `blockstack.id`, once you are signed in: |
|||
|
|||
![](images/running-app.png) |
|||
|
|||
## Create the hello-android project |
|||
|
|||
In this section, you'll create an Android application in Android Studio. You'll |
|||
run the application in the emulator to test it. |
|||
|
|||
### Create a simple project |
|||
|
|||
In this section, you create an inital project. You'll validate the application's |
|||
iniatial state by creating an emulator to run it in. Open Android Studio and do the following: |
|||
|
|||
1. Open Android Studio and choose **Start a new Andriod Studio project**. |
|||
|
|||
If studio is already started, choose **File > New > New Project**. |
|||
|
|||
2. Choose a project template: select **Empty Activity** and press **Next** |
|||
|
|||
3. Enter these fields in the **Create Android Project** page. |
|||
|
|||
| Name | Hello Android | |
|||
| ---------------- | --------------------------------------------------- | |
|||
| Package name | `blockstack.id.USERNAME.hello` | |
|||
| Project location | `/home/USERNAME/AndroidStudioProjects/helloandroid` | |
|||
| Language | Select (Kotlin) | |
|||
| Minimum SDK | Select (API 21: Android 5.0 (Lollipop)) | |
|||
|
|||
![](images/configure-activity.png) |
|||
|
|||
4. Press **Finish**. |
|||
|
|||
Android studio builds your initial project. This can take a bit the first time you do it. |
|||
|
|||
### Run the app in an emulator |
|||
|
|||
In this section, you run the appliation and create an emulator when prompted. |
|||
|
|||
1. Once the project is imported into studio, click the `app` module in the **Project** window. |
|||
|
|||
2. Then, select **Run > Run** (or click the green arrow in the toolbar). |
|||
|
|||
Studio prompts you to **Select Deployment Target**. |
|||
|
|||
3. Choose **Create New Virtual Device** and press **OK**. |
|||
|
|||
Studio prompts you to **Select Hardware**. |
|||
|
|||
4. Choose a Phone device configuration like "Pixel". |
|||
|
|||
![](images/select-hdw.png) |
|||
|
|||
Studio prompts you for a system image. |
|||
|
|||
5. Choose **Q** which is API level 29 and press **Next**. |
|||
|
|||
![](images/q-api.png) |
|||
|
|||
Studio asks you to verify your new emulator configuration. |
|||
|
|||
6. Press **Finish**. |
|||
|
|||
The emulation takes a moment to build. Then, studio launches the emulation and opens your application. |
|||
|
|||
![](images/hello-andriod-1.png) |
|||
|
|||
### Configure your application with the Blockstack SDK |
|||
|
|||
Now that you have created your initial project and verified it running in an emulator, you are ready to begin configuring the application for use with Blockstack. |
|||
|
|||
1. In studio, open the `AndroidManifest.xml` file. |
|||
|
|||
2. Add an `<intent-filter>` with the custom handler for Blockstack. Replace the host ("flamboyant-darwin-d11c17.netlify.app") with your own url once you have hosted your web app publicly. |
|||
|
|||
```xml |
|||
<intent-filter> |
|||
<action android:name="android.intent.action.VIEW" /> |
|||
<category android:name="android.intent.category.DEFAULT" /> |
|||
<category android:name="android.intent.category.BROWSABLE" /> |
|||
<data android:scheme="https" android:host="flamboyant-darwin-d11c17.netlify.app" /> |
|||
</intent-filter> |
|||
``` |
|||
|
|||
3. Open the Project's `build.gradle` file ("Project: Hello Android"). |
|||
4. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. for all projects |
|||
|
|||
When you finish, that section looks like this: |
|||
|
|||
```js |
|||
allprojects { |
|||
repositories { |
|||
google() |
|||
jcenter() |
|||
maven { url 'https://jitpack.io' } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
5. Open the Module `build.gradle` file ("Module: app"). |
|||
6. Fix a problem with duplicate meta data in the Android SDK: add `packagingOptions` |
|||
|
|||
```js |
|||
android { |
|||
... |
|||
packagingOptions { |
|||
exclude 'META-INF/*' |
|||
} |
|||
} |
|||
``` |
|||
|
|||
7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list: |
|||
|
|||
When you are done you should see: |
|||
|
|||
```js |
|||
dependencies { |
|||
implementation fileTree(dir: 'libs', include: ['*.jar']) |
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
|||
implementation 'androidx.appcompat:appcompat:1.1.0' |
|||
implementation 'androidx.core:core-ktx:1.2.0' |
|||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' |
|||
testImplementation 'junit:junit:4.12' |
|||
androidTestImplementation 'androidx.test.ext:junit:1.1.1' |
|||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' |
|||
|
|||
implementation 'com.github.blockstack:blockstack-android:7940940' |
|||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0' |
|||
} |
|||
|
|||
``` |
|||
|
|||
**NOTE**: Ignore the warning on the `junit` dependencies. |
|||
|
|||
8. Sync your project. |
|||
|
|||
![](images/sync-project.png) |
|||
|
|||
Be sure to check the sync completed successfully. |
|||
|
|||
![](images/sync-success.png) |
|||
|
|||
9. Run your app in the emulator. |
|||
|
|||
You've made a lot of changes, make sure the emulator is still running |
|||
correctly. |
|||
|
|||
### Add a simple interface |
|||
|
|||
1. Open the `app/res/layout/activity_main.xml` file. |
|||
|
|||
The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code. |
|||
|
|||
2. Replace the entire content of the file with the following code: |
|||
|
|||
The new interface includes a `BlockstackSignInButton` which is provided by |
|||
the SDK. This SDK includes a themed "Get Started" button |
|||
(`BlockstackSignInButton`). You use this button here with the |
|||
`org.blockstack.android.sdk.ui.BlockstackSignInButton` class. |
|||
|
|||
```xml |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:layout_margin="16dp" |
|||
tools:context=".MainActivity"> |
|||
|
|||
<org.blockstack.android.sdk.ui.BlockstackSignInButton |
|||
android:id="@+id/signInButton" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
app:layout_constraintTop_toTopOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintBottom_toTopOf="@id/userDataTextView"/> |
|||
|
|||
<TextView |
|||
android:id="@+id/userDataTextView" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="Hello World!" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintLeft_toLeftOf="parent" |
|||
app:layout_constraintRight_toRightOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/signInButton" /> |
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
``` |
|||
|
|||
This codes adds a button and some text to your application. |
|||
|
|||
3. Choose **Run > Apply changes**. |
|||
|
|||
4. Choose **Run > Run app** in the emulator. |
|||
|
|||
The emulator now contains a new interface with a button: |
|||
|
|||
![](images/new-interface.png) |
|||
|
|||
### Add session & authentication code |
|||
|
|||
1. Open the `MainActivity.kt` file. |
|||
2. Add some additional imports to the top, replace the `android.os.Bundle` import. |
|||
|
|||
When you are done, your imports should appear as follows: |
|||
|
|||
```kotlin |
|||
import android.content.Intent |
|||
import android.os.Bundle |
|||
import androidx.appcompat.app.AppCompatActivity |
|||
import androidx.lifecycle.lifecycleScope |
|||
import kotlinx.android.synthetic.main.activity_main.* |
|||
import kotlinx.coroutines.Dispatchers |
|||
import kotlinx.coroutines.launch |
|||
import org.blockstack.android.sdk.BlockstackSession |
|||
import org.blockstack.android.sdk.BlockstackSignIn |
|||
import org.blockstack.android.sdk.SessionStore |
|||
import org.blockstack.android.sdk.getBlockstackSharedPreferences |
|||
import org.blockstack.android.sdk.model.UserData |
|||
import org.blockstack.android.sdk.model.toBlockstackConfig |
|||
import org.blockstack.android.sdk.ui.SignInProvider |
|||
import org.blockstack.android.sdk.ui.showBlockstackConnect |
|||
``` |
|||
|
|||
3. Add one variable for the Blockstack sign-in flow and one for the Blockstack session that deals with all other function. This needs to be added before `onCreate`. |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity() { |
|||
|
|||
private lateinit var blockstackSession: BlockstackSession |
|||
private lateinit var blockstackSignIn: BlockstackSignIn |
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Replace the existing the `onCreate` function with the following, use your own domain if you are already hosting your web app somewhere: |
|||
|
|||
```kotlin |
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
val appConfig = |
|||
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig() |
|||
val sessionStore = SessionStore(getBlockstackSharedPreferences()) |
|||
blockstackSession = BlockstackSession(sessionStore, appConfig) |
|||
blockstackSignIn = BlockstackSignIn(sessionStore, appConfig) |
|||
BlockstackSignIn.shouldLaunchInCustomTabs = false |
|||
|
|||
signInButton.setOnClickListener { |
|||
showBlockstackConnect() |
|||
} |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
// handle the redirect from sign in |
|||
userDataTextView.text = "Signing in now ..." |
|||
lifecycleScope.launch(Dispatchers.IO) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This new `onCreate` does several things: |
|||
|
|||
- Supply authentication information for connecting to your Blockstack app: `appDomain` (for `scopes`, `redirectURI`, `manifestURI` the default values are used) |
|||
- Add a listener for the button click. |
|||
- Handles the redirect intent from the sign-in flow. |
|||
|
|||
Notice that the application in this example is a URI you have not set up. |
|||
Registering and application name takes time, so in time's interest you'll |
|||
use an existing app (https://flamboyant-darwin-d11c17.netlify.app) that is identical to the `hello-world` you created |
|||
earlier. For a production version, you'll need to replace `appDomain`, |
|||
`redirectURI`, `manifestURI` and `scopes` with values appropriate for your |
|||
app. |
|||
|
|||
5. Add a private function to reflect when a user successfully signs in. |
|||
|
|||
```kotlin |
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
signInButton.isEnabled = false |
|||
} |
|||
``` |
|||
|
|||
6. Create a handler for the authentication response. |
|||
|
|||
```kotlin |
|||
private fun handleAuthResponse(intent: Intent) { |
|||
val authResponse = intent.data?.getQueryParameter("authResponse") |
|||
if (authResponse != null) { |
|||
val userData = blockstackSession.handlePendingSignIn(authResponse) |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
7. Add `SignInProvider` interface for better user onboarding. Let the activity implement the interface and implement the member function `getBlockstackSignIn` |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity(), SignInProvider { |
|||
... |
|||
|
|||
override fun provideBlockstackSignIn(): BlockstackSignIn { |
|||
return blockstackSignIn |
|||
} |
|||
} |
|||
``` |
|||
|
|||
8. Verify your final `MainActivity.kt` code looks like this: |
|||
|
|||
```kotlin |
|||
package blockstack.id.user.hello |
|||
|
|||
import android.content.Intent |
|||
import android.os.Bundle |
|||
import androidx.appcompat.app.AppCompatActivity |
|||
import androidx.lifecycle.lifecycleScope |
|||
import kotlinx.android.synthetic.main.activity_main.\* |
|||
import kotlinx.coroutines.Dispatchers |
|||
import kotlinx.coroutines.launch |
|||
import org.blockstack.android.sdk.BlockstackSession |
|||
import org.blockstack.android.sdk.BlockstackSignIn |
|||
import org.blockstack.android.sdk.SessionStore |
|||
import org.blockstack.android.sdk.getBlockstackSharedPreferences |
|||
import org.blockstack.android.sdk.model.UserData |
|||
import org.blockstack.android.sdk.model.toBlockstackConfig |
|||
import org.blockstack.android.sdk.ui.SignInProvider |
|||
import org.blockstack.android.sdk.ui.showBlockstackConnect |
|||
|
|||
class MainActivity : AppCompatActivity(), SignInProvider { |
|||
|
|||
private lateinit var blockstackSession: BlockstackSession |
|||
private lateinit var blockstackSignIn: BlockstackSignIn |
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
val appConfig = |
|||
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig() |
|||
val sessionStore = SessionStore(getBlockstackSharedPreferences()) |
|||
blockstackSession = BlockstackSession(sessionStore, appConfig) |
|||
blockstackSignIn = BlockstackSignIn(sessionStore, appConfig) |
|||
BlockstackSignIn.shouldLaunchInCustomTabs = false |
|||
|
|||
signInButton.setOnClickListener { |
|||
showBlockstackConnect() |
|||
} |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
// handle the redirect from sign in |
|||
userDataTextView.text = "Signing in now ..." |
|||
lifecycleScope.launch(Dispatchers.IO) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
} |
|||
|
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
signInButton.isEnabled = false |
|||
} |
|||
|
|||
private suspend fun handleAuthResponse(intent: Intent) { |
|||
val authResponse = intent.data?.getQueryParameter("authResponse") |
|||
if (authResponse != null) { |
|||
val userData = blockstackSession.handlePendingSignIn(authResponse) |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
override fun provideBlockstackSignIn(): BlockstackSignIn { |
|||
return blockstackSignIn |
|||
} |
|||
|
|||
} |
|||
``` |
|||
|
|||
### Run the final app in the emulator |
|||
|
|||
1. Choose **Run > Apply changes**. |
|||
2. Choose **Run > Run app** in the emulator. |
|||
3. When you see the application open, choose **Get Started**. |
|||
4. A small information is presented about the sign-in flow |
|||
|
|||
![](images/connect-ui.png) |
|||
|
|||
5. Select **Get Started** |
|||
The system might prompt you how to select a browser. |
|||
|
|||
6. Work through the Blockstack prompts to login. |
|||
7. Blockstack redirects you to a web site. Open it with your Android app: Select **Hello Android** and **Always** |
|||
|
|||
![](images/connect-response.png) |
|||
|
|||
8. The screen after the sign-in flow shows the owner address of the username that was entered during sign-in. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
## Where to go next |
|||
|
|||
Congratulations, you've completed your Android app using the Blockstack Android SDK. |
|||
|
|||
Learn more about Blockstack by [trying another tutorial](https://blockstack.org/tutorials). |
@ -1,12 +1,14 @@ |
|||
--- |
|||
title: Building a Todo app |
|||
description: Learn how to integrate authentication and data storage. |
|||
tags: |
|||
- tutorial |
|||
redirect_from: |
|||
- /develop/zero_to_dapp_1.html |
|||
- /browser/hello-blockstack.html |
|||
--- |
|||
|
|||
# Integrating Blockstack |
|||
# Building a Todo app |
|||
|
|||
In this tutorial, you will learn about Blockstack authentication and storage by installing, |
|||
running and reviewing the code for a "Todos" web app built with Blockstack and [React](https://reactjs.org/). |
@ -1,3 +1,8 @@ |
|||
--- |
|||
description: 'Guide to Blockstack Connect' |
|||
experience: beginners |
|||
--- |
|||
|
|||
# Guide to Blockstack Connect |
|||
|
|||
## Installation |
@ -1,13 +1,16 @@ |
|||
--- |
|||
title: Guide to Blockstack Profiles |
|||
--- |
|||
|
|||
# Guide to Blockstack Profiles |
|||
|
|||
You can use the blockstack.js library to create and register an Blockstack username on the Stacks blockchain. This section describes the `Profile` object and contains the following topics: |
|||
You can use `blockstack.js` to create and register an Blockstack username on the Stacks blockchain. |
|||
This section describes the `Profile` object and contains the following topics: |
|||
|
|||
## About profiles |
|||
|
|||
Profile data is stored using Gaia on the user's selected storage provider. An example of a `profile.json` file URL using Blockstack provided storage: |
|||
Profile data is stored using Gaia on the user's selected storage provider. An example of a `profile.json` file URL using |
|||
Blockstack provided storage: |
|||
|
|||
``` |
|||
https://gaia.blockstack.org/hub/1EeZtGNdFrVB2AgLFsZbyBCF7UTZcEWhHk/profile.json |
@ -0,0 +1,76 @@ |
|||
--- |
|||
title: Building decentralized apps |
|||
description: Overview and guides for getting started building decentralized applications. |
|||
--- |
|||
|
|||
# Building decentralized apps |
|||
|
|||
Prefer to jump right in? Get started with this tutorial where you’ll create a decentralized to-do list app. |
|||
|
|||
[[page-reference | inline]] |
|||
| /authentication/building-todo-app |
|||
|
|||
## What are decentralized apps? |
|||
|
|||
Decentralized apps are apps that don’t depend on a centralized platform, server or database. Instead, they use a |
|||
decentralized network, built on the Stacks blockchain, for authentication, data storage, and backend logic. Just like |
|||
Bitcoin, a decentralized network of applications is accessible to anyone and not controlled by any central authority. |
|||
|
|||
To learn more about the Blockstack network and decentralization, read the Blockstack overview. |
|||
|
|||
### User-owned data |
|||
|
|||
Data is stored with the user and encrypted with a key that only they own. Developers aren’t responsible for, or have to |
|||
host, their users’ data. This protects users against security breaches and keeps their data private. |
|||
|
|||
### Smart contracts |
|||
|
|||
Decentralized apps can use smart contracts to make their backend logic public, open, and permissionless. Once published |
|||
on the blockchain, no one really owns or controls a smart contract. They will execute when their terms are met, |
|||
regardless of who interacts with it. |
|||
|
|||
### Compatible and extendable |
|||
|
|||
Decentralized apps are compatible by nature because they use the same data and shared state. You can build on top of |
|||
other apps without requiring permission or fear of being shut out. |
|||
|
|||
## Getting started |
|||
|
|||
To build your decentralized app, you’ll use authentication, data storage, data indexing (optional), and smart contracts |
|||
(optional). Get started with the documentation and tutorials below. |
|||
|
|||
### Authentication and data storage |
|||
|
|||
Like a regular app, yours will require user authentication and data storage — but decentralized. Get started with the |
|||
documentation below or try the tutorial. |
|||
|
|||
[[page-reference | inline]] |
|||
| /authentication/overview |
|||
|
|||
[[page-reference | inline]] |
|||
| /data-storage/overview |
|||
|
|||
[[page-reference | inline]] |
|||
| /authentication/building-todo-app |
|||
|
|||
### Data indexing |
|||
|
|||
If you need to store and index data shared by multiple users, such as messages or a shared document, read the Radiks |
|||
documentation. |
|||
|
|||
[[page-reference | inline]] |
|||
| /data-indexing/overview |
|||
|
|||
### Smart contracts |
|||
|
|||
You can use smart contracts to decentralize your app’s backend logic, making it open and permissionless. Smart contracts |
|||
on Blockstack are written in the Clarity language. View the smart contracts documentation or get started with a tutorial. |
|||
|
|||
[[page-reference | inline]] |
|||
| /smart-contracts/overview |
|||
|
|||
[[page-reference | inline]] |
|||
| /smart-contracts/hello-world-tutorial |
|||
|
|||
[[page-reference | inline]] |
|||
| /smart-contracts/counter-tutorial |
@ -1,5 +0,0 @@ |
|||
--- |
|||
layout: externalurl |
|||
redirect_url: https://blockstack.github.io/blockstack-android/ |
|||
title: 'Blockstack Android Reference' |
|||
--- |
@ -1,5 +0,0 @@ |
|||
--- |
|||
layout: externalurl |
|||
redirect_url: https://community.blockstack.org/ |
|||
title: 'Blockstack Community' |
|||
--- |
@ -1,5 +0,0 @@ |
|||
--- |
|||
layout: externalurl |
|||
redirect_url: https://blockstack.github.io/blockstack-ios/ |
|||
title: 'Blockstack iOS Reference' |
|||
--- |
@ -1,5 +0,0 @@ |
|||
--- |
|||
layout: externalurl |
|||
redirect_url: https://blockstack.github.io/blockstack.js/ |
|||
title: 'Blockstack Javascript Reference' |
|||
--- |
@ -1,9 +0,0 @@ |
|||
--- |
|||
title: Got Any Questions? |
|||
permalink: /contact/ |
|||
--- |
|||
|
|||
##### To get in touch with us... |
|||
|
|||
Please feel free to post any [questions on our public forum](https://forum.blockstack.org/). |
|||
You can also request [access to our Slack instance](https://docs.google.com/a/blockstack.com/forms/d/e/1FAIpQLSed5Mnu0G5ZMJdWs6cTO_8sTJfUVfe1sYL6WFDcD51_XuQkZw/viewform). |
@ -1,20 +0,0 @@ |
|||
--- |
|||
description: Blockstack DApp technical FAQs |
|||
--- |
|||
|
|||
export { convertFaqAnswersToMDX as getStaticProps } from '@common/data/faq' |
|||
import { FAQs } from '@components/faq' |
|||
|
|||
# DApp Developer FAQs |
|||
|
|||
This document lists frequently-asked questions developers about Blockstack application development. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first. |
|||
|
|||
For more technical FAQs about Stacks nodes, the Stacks blockchain, and other architectural elements, see the [entire set of technical FAQs](/core/faq_technical). |
|||
|
|||
If you have a technical question that gets frequently asked on the |
|||
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com), |
|||
feel free to send a pull-request with the question and answer. |
|||
|
|||
<FAQs category="appdevs" data={props.mdx} /> |
|||
|
|||
<FAQs category="opensource" data={props.mdx} /> |
@ -1,26 +0,0 @@ |
|||
export { convertFaqAnswersToMDX as getStaticProps } from '@common/data/faq' |
|||
import { FAQs } from '@components/faq' |
|||
|
|||
# Technical FAQ |
|||
|
|||
This document lists frequently-asked questions by developers interested in working with Blockstack application and core components. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first. |
|||
|
|||
If you have a technical question that gets frequently asked on the |
|||
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com), |
|||
feel free to send a pull-request with the question and answer. |
|||
|
|||
## DApp developers |
|||
|
|||
<FAQs category="appdevs" data={props.mdx} /> |
|||
|
|||
## Core developers |
|||
|
|||
<FAQs category="coredevs" data={props.mdx} /> |
|||
|
|||
## Open source developers |
|||
|
|||
<FAQs category="opensource" data={props.mdx} /> |
|||
|
|||
## Miscellaneous questions |
|||
|
|||
<FAQs category="miscquest" data={props.mdx} /> |
@ -1,4 +1,6 @@ |
|||
--- |
|||
title: Create and use models |
|||
description: Model and query application data with Radiks. |
|||
--- |
|||
|
|||
# Create and use models |
@ -1,4 +1,6 @@ |
|||
--- |
|||
title: Overview |
|||
description: Using Radiks you can build multi-player apps that index, store, and query user data. |
|||
--- |
|||
|
|||
# Radiks the data indexer |
@ -1,4 +1,6 @@ |
|||
--- |
|||
title: Radiks server tips and tricks |
|||
description: Some tips and tricks for working with a Radiks server. |
|||
--- |
|||
|
|||
# Radiks server tips and tricks |
@ -1,4 +1,5 @@ |
|||
--- |
|||
title: Create a Collection type |
|||
--- |
|||
|
|||
# How to create a Collection type |
@ -1,4 +1,5 @@ |
|||
--- |
|||
title: Collections (Preview) |
|||
--- |
|||
|
|||
# Work with Collections (Preview) |
@ -0,0 +1,116 @@ |
|||
--- |
|||
title: Guide to Blockstack Storage |
|||
--- |
|||
|
|||
# Guide to Blockstack Storage |
|||
|
|||
The Blockstack Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the |
|||
Blockstack blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures |
|||
that Blockstack applications can provide users with high performance and high availability for data reads and writes |
|||
without introducing central trust parties. |
|||
|
|||
-> Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis. Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates. |
|||
|
|||
## How data is stored |
|||
|
|||
Gaia storage is a key-value store. |
|||
|
|||
## Creating a file |
|||
|
|||
Use the [UserSession.putFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile) method: |
|||
|
|||
```tsx |
|||
const userSession = new UserSession(); |
|||
const options: PutFileOptions = { |
|||
encrypt: false, |
|||
}; |
|||
userSession.putFile('/hello.txt', 'hello world!', options).then(() => { |
|||
// /hello.txt exists now, and has the contents "hello world!". |
|||
}); |
|||
``` |
|||
|
|||
## Creating an encrypted file |
|||
|
|||
Use the [UserSession.putFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile) method and |
|||
pass `encrypt: true` within the options object. See the [`PutFileOptions` type definition here](https://blockstack.github.io/blockstack.js/interfaces/putfileoptions.html#encrypt) |
|||
|
|||
```tsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options: PutFileOptions = { |
|||
encrypt: true, |
|||
}; |
|||
|
|||
userSession.putFile('/message.txt', 'Secret hello!', options).then(() => { |
|||
// message.txt exists now, and has the contents "hello world!". |
|||
}); |
|||
``` |
|||
|
|||
## Reading a file |
|||
|
|||
Use the [UserSession.getFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile) method: |
|||
|
|||
```tsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options: PutFileOptions = { |
|||
decrypt: false, |
|||
}; |
|||
|
|||
userSession.getFile('/hello.txt', options).then(fileContents => { |
|||
// get the contents of the file /hello.txt |
|||
assert(fileContents === 'hello world!'); |
|||
}); |
|||
``` |
|||
|
|||
## Reading an encrypted file |
|||
|
|||
Use the [UserSession.getFile](https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile) method and pass |
|||
`decrypt: true` within the options object. See the [`GetFileOptions` type definition here](https://blockstack.github.io/blockstack.js/interfaces/getfileoptions.html#decrypt) |
|||
|
|||
```tsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options: GetFileOptions = { |
|||
decrypt: true, |
|||
}; |
|||
|
|||
userSession.getFile('/message.txt', options).then(fileContents => { |
|||
// get & decrypt the contents of the file /message.txt |
|||
assert(fileContents === 'Secret hello!'); |
|||
}); |
|||
``` |
|||
|
|||
## Reading another user's unencrypted file |
|||
|
|||
In order for files to be publicly readable, the app must request |
|||
the [`publish_data` scope](https://blockstack.github.io/blockstack.js/enums/authscope.html#publish_data) during authentication. |
|||
|
|||
```jsx |
|||
const options = { |
|||
user: 'ryan.id', // the Blockstack ID of the user for which to lookup the file |
|||
app: 'https://BlockstackApp.com', // origin of the app this file is stored for |
|||
}; |
|||
|
|||
const userSession = new UserSession(); |
|||
userSession.putFile('/hello.txt', 'hello world!', options).then(fileContents => { |
|||
// get the contents of the file /message.txt |
|||
assert(fileContents === 'hello world!'); |
|||
}); |
|||
``` |
|||
|
|||
## Delete a file |
|||
|
|||
Use the [`UserSession.deleteFile`](https://blockstack.github.io/blockstack.js/classes/usersession.html#deletefile) from the application's data store. |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
|
|||
userSession.deleteFile('/hello.txt').then(() => { |
|||
// /hello.txt is now removed. |
|||
}); |
|||
``` |
|||
|
|||
## Related Information |
|||
|
|||
To learn more about the guarantees provided by Gaia, see [Storage write and read](/storage/write-to-read) |
@ -1,191 +0,0 @@ |
|||
--- |
|||
description: 'JavaScript library for integration authentication and smart contracts' |
|||
redirect_from: |
|||
- /develop/connect/get-started.html |
|||
--- |
|||
|
|||
# Guide to Blockstack Connect |
|||
|
|||
## Introduction |
|||
|
|||
Blockstack Connect is a JavaScript library for integrating Blockstack authentication and smart contracts into your app. |
|||
|
|||
The library empowers you to: |
|||
|
|||
- Register new users with a pre-built onboarding flow that quickly educates them as to the privacy benefits of using your app with Blockstack and provisions a "Secret Key" that secures their identity and data against the Stacks blockchain. |
|||
- Authenticate users when they return to your app using that same Secret Key. |
|||
- Prompt users to sign transactions with smart contracts as written in Clarity and published to the Stacks blockchain. |
|||
|
|||
## How does this compare to `blockstack.js`? |
|||
|
|||
Although [`blockstack.js`](https://github.com/blockstack/blockstack.js) can also be used to authenticate users, it implements the deprecated [Blockstack Browser](https://browser.blockstack.org/) and lacks any pre-built onboarding UI that educates users as to how your app is more secure for having implemented Blockstack. As such, we advise that you use `blockstack.js` for all other functionality apart from authentication, such as saving and retrieving user data with Gaia. |
|||
|
|||
## Start building with Blockstack Connect |
|||
|
|||
Head over to the [Tutorial for App Integration](/browser/todo-list) to learn how to build apps with Blockstack Connect. |
|||
|
|||
## Installation |
|||
|
|||
With yarn: |
|||
|
|||
```bash |
|||
yarn add @blockstack/connect |
|||
``` |
|||
|
|||
With npm: |
|||
|
|||
```bash |
|||
npm install --save @blockstack/connect |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
### AuthOptions |
|||
|
|||
Every major method you'll use with `connect` requires you to pass some options, like the name and icon of your app, and what to do when authentication is finished. In practice, this means you need to define these options, and pass them to the various API methods. |
|||
|
|||
The exact interface you'll use [is defined as](https://github.com/blockstack/connect/blob/master/src/auth.ts#L12:L24): |
|||
|
|||
```tsx |
|||
export interface AuthOptions { |
|||
redirectTo: string; |
|||
finished: (payload: FinishedData) => void; |
|||
sendToSignIn?: boolean; |
|||
userSession?: UserSession; |
|||
appDetails: { |
|||
name: string; |
|||
icon: string; |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
| parameter | type | default | optional | description | |
|||
| ------------ | ----------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
|||
| redirectTo | string | | false | The path in your app where users go after sign in. | |
|||
| appDetails | object | | false | an object which includes `appName: string` and `appIcon: string`. This will speed up the process of loading your app's information during onboarding. | |
|||
| finished | function | | false | A callback that can be invoked after authentication. This prevents having to do a whole page refresh in a new tab. One argument is passed to this callback, which is an object with `userSession` included. If included, then the `redirectTo` path is ignored, and the user will be logged in automatically. | |
|||
| sendToSignIn | boolean | false | true | Whether the user should go straight to the 'sign in' flow (true) or be presented with the 'sign up' flow (false) instead. | |
|||
| userSession | UserSession | | false | pass a `UserSession` instance to use for authentication. If it's not passed, `@blockstack/connect` will create one for you. | |
|||
|
|||
### In React Apps |
|||
|
|||
If you're using `connect` in a React app, then the best option is to include `connect`'s React Provider and hooks in your React app. |
|||
|
|||
First, setup the `Connect` provider at the "top-level" of your app - probably next to wherever you would put a Redux provider, for example. |
|||
|
|||
```tsx |
|||
import { Connect } from '@blockstack/connect'; |
|||
|
|||
const authOptions = { |
|||
redirectTo: '/', |
|||
finished: ({ userSession }) => { |
|||
console.log(userSession.loadUserData()); |
|||
}, |
|||
appDetails: { |
|||
name: 'My Cool App', |
|||
icon: 'https://example.com/icon.png', |
|||
}, |
|||
}; |
|||
|
|||
const App: React.FC<AppProps> = props => { |
|||
return <Connect authOptions={authOptions}>{/** the rest of your app's components */}</Connect>; |
|||
}; |
|||
``` |
|||
|
|||
Later, when you want to begin the onboarding process, use the `useConnect` hook to get `connect`'s `doOpenAuth` method. |
|||
|
|||
```tsx |
|||
import { useConnect } from '@blockstack/connect'; |
|||
|
|||
const SignInButton = () => { |
|||
const { doOpenAuth } = useConnect(); |
|||
|
|||
return <Button onClick={doOpenAuth}>Sign In</Button>; |
|||
}; |
|||
``` |
|||
|
|||
#### Sign In |
|||
|
|||
To send the user straight to sign in, call `doOpenAuth(true)`. |
|||
|
|||
### In ES6 (non-React) apps |
|||
|
|||
If you aren't using React, or just want a simpler API, then you can use the `showBlockstackConnect` method. |
|||
|
|||
```tsx |
|||
import { showBlockstackConnect } from '@blockstack/connect'; |
|||
|
|||
const authOptions = { |
|||
/** See docs above for options */ |
|||
}; |
|||
showBlockstackConnect(authOptions); |
|||
``` |
|||
|
|||
#### Sign In |
|||
|
|||
To send the user straight to sign in, include `sendToSignIn: true` in your `authOptions`. |
|||
|
|||
#### Note about dependency size: |
|||
|
|||
If you're building a non-React app, note that importing `@blockstack/connect` will add React dependencies to your JavaScript bundle. We recommend using something like [Webpack resolve aliases](https://webpack.js.org/configuration/resolve/) to replace `react` with `preact` in production, which reduces your bundle size. Check out [our own webpack.config.js file](https://github.com/blockstack/ux/blob/fix/connect-modal-accessibility/packages/connect/webpack.config.js#L87:L95) to see how we use this for production builds. |
|||
|
|||
If you're using the hosted version of `@blockstack/connect` (described below), then you already have a production-optimized bundle. |
|||
|
|||
### Using a hosted version of `@blockstack/connect` |
|||
|
|||
If you aren't using ES6 imports, you can still use `connect`! We package the library so that it can be automatically used with [unpkg](https://unpkg.com/). |
|||
|
|||
First, include the script in your HTML: |
|||
|
|||
```html |
|||
<script src="https://unpkg.com/@blockstack/connect"> |
|||
``` |
|||
|
|||
Then, you can use API methods under the `blockstackConnect` global variable: |
|||
|
|||
```jsx |
|||
const authOptions = { |
|||
/** See docs above for options */ |
|||
}; |
|||
blockstackConnect.showBlockstackConnect(authOptions); |
|||
``` |
|||
|
|||
## Handling redirect fallbacks |
|||
|
|||
Connect is built to use popups with the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API, which provides a much better and seamless user experience. However, there are times when this flow can fail. For example, the popup may be blocked, or the `window.postMessage` API might not work properly (which often happens on mobile browsers). |
|||
|
|||
To make sure your app handles this gracefully, you'll need to handle the case where authentication is performed through regular HTTP redirects. With redirects, your users will be sent back to your app at a URL like: |
|||
|
|||
`${authOptions.redirectTo}?authResponse=....` |
|||
|
|||
To finalize authentication with this flow, you'll need to utilize the `UserSession` methods `isSignInPending()` and `handlePendingSignIn()`. For more information, check out the [blockstack.js API reference](https://blockstack.github.io/blockstack.js/). |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(appConfig); |
|||
|
|||
// ... call this code on page load |
|||
if (userSession.isSignInPending()) { |
|||
const userData = await userSession.handlePendingSignIn(); |
|||
// your user is now logged in. |
|||
} |
|||
``` |
|||
|
|||
## Design Guidance |
|||
|
|||
Blockstack is valuable to users, but it can also be a barrier to those unfamiliar with Blockstack. The following guidelines serve to remedy that and help you onboard as many new users as you can. |
|||
|
|||
### Delay Blockstack onboarding as long as possible |
|||
|
|||
People will often leave apps when things are asked of them before they experience the app. Give them a chance to try your app before you ask them to sign up with Blockstack. For example, a note taking app could let a new user write a couple of notes before prompting them to save their progress. |
|||
|
|||
### Provide an easy way in for new users |
|||
|
|||
Many new users to your app will not be familiar with Blockstack yet and will be hesitant to click a Blockstack-branded button. Provide a generic button for users that are new to your app and Blockstack. Blockstack Connect will introduce new users to Blockstack and recognize existing users. |
|||
|
|||
[![Design Guidance Example](/develop/images/connect-call-to-action-branding.png)](/develop/images/connect-call-to-action-branding.png) |
|||
|
|||
### Provide a quick way for existing users to sign in |
|||
|
|||
You can point users to a specific part of the Blockstack App. For instance, a “Sign in” button on your website can redirect users to the sign in flow of the Blockstack App. If you do this, make sure you also have an option that is explicitly for new users and that points to the sign up flow. |
|||
|
|||
To implement this functionality, check out our section on sending users to sign in immediately. |
@ -1,4 +0,0 @@ |
|||
--- |
|||
layout: null |
|||
--- |
|||
{{ site.data.appFAQ | jsonify }} |
@ -1,111 +0,0 @@ |
|||
--- |
|||
--- |
|||
|
|||
# Guide to Blockstack Storage |
|||
|
|||
The Blockstack Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the Blockstack blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures that Blockstack applications can provide users with high performance and high availability for data reads and writes without introducing central trust parties. |
|||
|
|||
> Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis. |
|||
> Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates. |
|||
|
|||
## How data is stored |
|||
|
|||
Gaia storage is a key-value store. |
|||
|
|||
## Creating a file |
|||
|
|||
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile" target="_blank">UserSession.putFile</a> |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
const options = { |
|||
encrypt: false, |
|||
}; |
|||
userSession.putFile('/hello.txt', 'hello world!', options).then(() => { |
|||
// /hello.txt exists now, and has the contents "hello world!". |
|||
}); |
|||
``` |
|||
|
|||
## Creating an encrypted file |
|||
|
|||
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile" target="_blank"></a> |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options = { |
|||
encrypt: true, |
|||
}; |
|||
|
|||
userSession.putFile('/message.txt', 'Secret hello!', options).then(() => { |
|||
// message.txt exists now, and has the contents "hello world!". |
|||
}); |
|||
``` |
|||
|
|||
## Reading a file |
|||
|
|||
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#getfile" target="_blank"></a> |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options = { |
|||
decrypt: false, |
|||
}; |
|||
|
|||
userSession.getFile('/hello.txt', options).then(fileContents => { |
|||
// get the contents of the file /hello.txt |
|||
assert(fileContents === 'hello world!'); |
|||
}); |
|||
``` |
|||
|
|||
## Reading an encrypted file |
|||
|
|||
You use the <a href="" target="_blank"></a> |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
|
|||
const options = { |
|||
decrypt: true, |
|||
}; |
|||
|
|||
userSession.getFile('/message.txt', options).then(fileContents => { |
|||
// get & decrypt the contents of the file /message.txt |
|||
assert(fileContents === 'Secret hello!'); |
|||
}); |
|||
``` |
|||
|
|||
## Reading another user's unencrypted file |
|||
|
|||
In order for files to be publicly readable, the app must request |
|||
the `publish_data` scope during authentication. |
|||
|
|||
```jsx |
|||
const options = { |
|||
user: 'ryan.id', // the Blockstack ID of the user for which to lookup the file |
|||
app: 'http://BlockstackApp.com', // origin of the app this file is stored for |
|||
}; |
|||
|
|||
const userSession = new UserSession(); |
|||
userSession.putFile('/hello.txt', 'hello world!', options).then(fileContents => { |
|||
// get the contents of the file /message.txt |
|||
assert(fileContents === 'hello world!'); |
|||
}); |
|||
``` |
|||
|
|||
## Delete a file |
|||
|
|||
You use the <a href="https://blockstack.github.io/blockstack.js/classes/usersession.html#deletefile" target="_blank">UserSession.deleteFile</a> from the application's data store. |
|||
|
|||
```jsx |
|||
const userSession = new UserSession(); |
|||
|
|||
userSession.deleteFile('/hello.txt').then(() => { |
|||
// /hello.txt is now removed. |
|||
}); |
|||
``` |
|||
|
|||
## Related Information |
|||
|
|||
To learn more about the guarantees provided by Gaia, see [Storage write and read](/storage/write-to-read) |
@ -1,5 +1,6 @@ |
|||
--- |
|||
description: 'Blockstack token holder documentation' |
|||
title: Stacks token |
|||
description: Learn about the native token of Blockstack |
|||
--- |
|||
|
|||
# Learn more about the Stacks token |
@ -1,700 +0,0 @@ |
|||
--- |
|||
description: How to use Blockstack on iOS Mobile |
|||
--- |
|||
|
|||
# iOS DApps |
|||
|
|||
This tutorial teaches you how to create a decentralized application using |
|||
Blockstack's iOS SDK using the following content: |
|||
|
|||
This tutorial was extensively tested using Xcode Version 11.2 (11B52) on a MacBook Pro |
|||
running Mojave 10.14. If your environment is different, you may encounter |
|||
slight or even major discrepancies when performing the procedures in this |
|||
tutorial. Please [join the Blockstack community Slack](https://slofile.com/slack/blockstack) and post questions or comments to |
|||
the `#support` channel. |
|||
|
|||
Finally, this tutorial is written for all levels from the beginner to the most |
|||
experienced. For best results, beginners should follow the guide as written. It |
|||
is expected that the fast or furiously brilliant will skip ahead and improvise |
|||
on this material at will. God speed one and all. |
|||
|
|||
If you want to download a complete application rather than working through a tutorial, see this _alternative_ sample, the <a href="https://github.com/blockstack/photoblock-demo" target="_blank">photoblock-demo</a>. |
|||
|
|||
## Understand the sample application flow |
|||
|
|||
When complete, the sample application is a simple `hello-world` intended for use |
|||
on an iOS phone. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
Only users with an existing `blockstack.id` can run your |
|||
final sample application. When complete, users interact with the sample |
|||
application by doing the following: |
|||
|
|||
![](images/app-flow.png) |
|||
|
|||
## Set up your environment |
|||
|
|||
This sample application requires two code bases, a BlockStack `hello-blockstack` web |
|||
application and a `hello-ios` iOS application. You use the iOS application to run the |
|||
web application on an iOS device. |
|||
|
|||
Before you start developing the sample, there are a few elements you need in |
|||
your environment. |
|||
|
|||
### Install XCode |
|||
|
|||
If you are an experienced iOS developer and already have an iOS |
|||
development environment on your workstation, you can use that and skip this |
|||
step. However, you may need to adjust the remaining instructions for your |
|||
environment. |
|||
|
|||
Follow the installation instructions to [download and install XCode](https://developer.apple.com/xcode/) for your operating system. |
|||
Depending on your network connection, this can take between 15-30 minutes. |
|||
|
|||
### Do you have Node.js? |
|||
|
|||
Node.js v10 or higher is recommended the minimum supported version is Node.js v8. Before you begin, verify you have the correct version of Node.js and its tools installed. |
|||
|
|||
```bash |
|||
$ node -v |
|||
v12.10.0 |
|||
$ which npm npx |
|||
/usr/local/bin/npm |
|||
/usr/local/bin/npx |
|||
``` |
|||
|
|||
If you don't have these installed, take a moment to install or upgrade as needed. |
|||
|
|||
### Install the CocoaPods 1.6.0 dependency manager |
|||
|
|||
The sample application only runs on devices with iOS 11.0 or higher. You install |
|||
the Blockstack iOS SDK through the CocoaPods. Cocoapods is a dependency manager |
|||
for Swift and Objective-C Cocoa projects. It’s a simple, user friendly way to |
|||
use libraries from the community in your project. |
|||
|
|||
You must use CocoaPods version `1.6.0` or newer to avoid an |
|||
incompatibility between Cocoapods and XCode. Before starting the tutorial, confirm |
|||
you have installed CocoaPods. |
|||
|
|||
```bash |
|||
$ pod --version |
|||
1.8.1 |
|||
``` |
|||
|
|||
If you don't have the CocoaPods beta version, install it: |
|||
|
|||
```bash |
|||
sudo gem install cocoapods -v 1.8.1 |
|||
``` |
|||
|
|||
## Build the Blockstack hello-world |
|||
|
|||
In this section, you build a Blockstack `hello-world` application. Then, you |
|||
modify the `hello-world` to interact with the iOS app via a redirect. |
|||
|
|||
### Generate and launch your hello-blockstack application |
|||
|
|||
@include "scaffolding.md" |
|||
|
|||
In this section, you build an initial React.js application called |
|||
`hello-blockstack`. |
|||
|
|||
1. Create a `hello-blockstack` directory. |
|||
|
|||
```bash |
|||
mkdir hello-blockstack |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-blockstack |
|||
``` |
|||
|
|||
3. Create your initial `hello-world-tutorial` application. |
|||
|
|||
```bash |
|||
$ npx generator-blockstack --react |
|||
npx: installed 338 in 13.792s |
|||
create package.json |
|||
create .gitignore |
|||
create webpack.config.js |
|||
create netlify.toml |
|||
create firebase.json |
|||
... |
|||
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself. |
|||
|
|||
> fsevents@1.2.9 install /private/tmp/testymc/node_modules/fsevents |
|||
> node install |
|||
added 775 packages from 455 contributors and audited 9435 packages in 20.934s |
|||
found 0 vulnerabilities |
|||
|
|||
``` |
|||
|
|||
Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section. |
|||
|
|||
4. Run the initial application. |
|||
|
|||
```bash |
|||
npm run start |
|||
|
|||
> hello-blockstack@0.0.0 start /Users/meepers/repos/hello-blockstack |
|||
> webpack-dev-server |
|||
|
|||
Project is running at http://localhost:8080/ |
|||
webpack output is served from / |
|||
404s will fallback to /index.html |
|||
Hash: 4d2312ba236a4b95dc3a |
|||
Version: webpack 2.7.0 |
|||
Time: 2969ms |
|||
Asset Size Chunks Chunk Names |
|||
.... |
|||
Child html-webpack-plugin for "index.html": |
|||
chunk {0} index.html 541 kB [entry] [rendered] |
|||
[0] ./~/lodash/lodash.js 540 kB {0} [built] |
|||
[1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built] |
|||
[2] (webpack)/buildin/global.js 509 bytes {0} [built] |
|||
[3] (webpack)/buildin/module.js 517 bytes {0} [built] |
|||
webpack: Compiled successfully. |
|||
``` |
|||
|
|||
At this point, the browser is running a Blockstack server on your local host. |
|||
|
|||
5. Navigate to `http://localhost:8080` with your browser to display the |
|||
application. |
|||
|
|||
![](/browser/images/initial-app.png) |
|||
|
|||
This local instances is for testing your applications only. |
|||
|
|||
6. Choose **Sign in with Blockstack** |
|||
|
|||
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one. |
|||
|
|||
![](/browser/images/login-choice.png) |
|||
|
|||
7. Follow the prompts appropriate to your situation. |
|||
|
|||
If you are restoring an existing ID, you may see a prompt about your user |
|||
being nameless, ignore it. At this point you have only a single application |
|||
on your test server. So, you should see this single application, with your |
|||
own `blockstack.id` display name, once you are signed in: |
|||
|
|||
![](/browser/images/hello-authd.png) |
|||
|
|||
### Add a redirect end point to your application |
|||
|
|||
When a user opens the webapp from the Blockstack Browser on an iOS phone, |
|||
you want the web app to redirect the user to your iOS application. The work |
|||
you do here will allow it. |
|||
|
|||
1. From the terminal command line, change directory to your web |
|||
application directory (`hello-blockstack`). |
|||
|
|||
1. If it doesn't ext, create the `public` directory. |
|||
|
|||
```bash |
|||
$ mkdir public |
|||
``` |
|||
|
|||
1. Use the `touch` command to add a redirect endpoint to your application. |
|||
|
|||
This endpoint on the web version of your app will redirect iOS users back |
|||
to your mobile app. |
|||
|
|||
```bash |
|||
$ touch public/redirect.html |
|||
``` |
|||
|
|||
1. Open `redirect.html` and add code to the endpoint. |
|||
|
|||
``` |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Hello, Blockstack!</title> |
|||
<script> |
|||
function getParameterByName(name) { |
|||
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); |
|||
return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); |
|||
} |
|||
|
|||
var authResponse = getParameterByName('authResponse') |
|||
window.location="myblockstackapp://?authResponse=" + authResponse |
|||
</script> |
|||
<body> |
|||
</body> |
|||
</html> |
|||
``` |
|||
|
|||
Blockstack apps are identified by their domain names. The endpoint will |
|||
receive a get request with the query parameter `authResponse=XXXX` and |
|||
should redirect the browser to `myblockstackapp:XXXX`. |
|||
|
|||
`myblockstackapp:` is custom protocol handler. The handler should be unique |
|||
to your application. Your app's web-based authentication uses this handler |
|||
to redirect the user back to your iOS app. Later, you'll add a reference |
|||
to this handler in your iOS application. |
|||
|
|||
1. Close and save the `redirect.html` file. |
|||
1. Ensure your Blockstack app compiles successfully. |
|||
|
|||
The `npm` process should detect and compile your change. |
|||
|
|||
## Build the hello-blockstack-ios |
|||
|
|||
Now, you build an iOS application that can work with your Blockstack web |
|||
application on a mobile device. |
|||
|
|||
### Create an XCode Project |
|||
|
|||
This tutorial uses XCode 11.1, you can use another version but be aware that some |
|||
menu items and therefore these procedures may be differœent on your version. |
|||
|
|||
1. Launch the XCode interface. |
|||
2. Choose **Create a new XCode project**. |
|||
3. Select **iOS**. |
|||
4. Select **Single View App**. |
|||
|
|||
![](images/single-view-app.png) |
|||
|
|||
5. On the **Choose options for your new project** dialog, set the following: |
|||
|
|||
| Product Name | `hello-blockstack-ios` | |
|||
| ----------------- | ---------------------- | |
|||
| Organization Name | USERNAME | |
|||
| User Interface | Storyboard | |
|||
|
|||
![](images/choose-new-options.png) |
|||
|
|||
6. Press **Next**. |
|||
|
|||
The system prompts you for a location to store your code. |
|||
|
|||
7. Save your project in your `hello-blockstack-ios` directory. |
|||
|
|||
8. Close XCode. |
|||
|
|||
### Add and edit a Podfile |
|||
|
|||
To use CocoaPods you need to define the XCode target to link them to. |
|||
So, for exampleM if you are writing an iOS app, it would be the name of your app. |
|||
Create a target section by writing target `$TARGET_NAME do` and an `end` a few |
|||
lines after. |
|||
|
|||
1. Open a terminal window on your workstation. |
|||
2. Change directory into your new project directory where your `hello-blockstack-ios.xcodeproj` file was created. |
|||
|
|||
```swift |
|||
$ cd hello-blockstack-ios |
|||
``` |
|||
|
|||
3. Create a Podfile. |
|||
|
|||
```bash |
|||
$ pod init |
|||
``` |
|||
|
|||
The command creates a `Podfile` in the directory. |
|||
|
|||
4. Open the `Podfile` for editing. |
|||
5. Add a line stating the Blockstack dependency. |
|||
|
|||
``` |
|||
# Uncomment the next line to define a global platform for your project |
|||
# platform :ios, '9.0' |
|||
|
|||
target 'hello-blockstack-ios' do |
|||
s |
|||
! |
|||
|
|||
s |
|||
' |
|||
|
|||
o |
|||
s |
|||
g |
|||
d |
|||
end |
|||
``` |
|||
|
|||
6. Save and close the `Podfile`. |
|||
|
|||
### Install Blockstack SDK and open the pod project |
|||
|
|||
1. Close your new XCode project if you haven't already. |
|||
2. In terminal, make sure it is open to the root of your `hello-blockstack-ios` project. |
|||
3. Initialize the project with Cocoapods via the `pod install` command. |
|||
|
|||
```bash |
|||
$ pod install |
|||
Analyzing dependencies |
|||
Downloading dependencies |
|||
Installing Blockstack (1.0.1) |
|||
Installing CryptoSwift (0.15.0) |
|||
Installing PromisesObjC (1.2.8) |
|||
Installing PromisesSwift (1.2.8) |
|||
Installing STRegex (2.1.0) |
|||
Generating Pods project |
|||
Integrating client project |
|||
|
|||
[!] Please close any current XCode sessions and use `hello-blockstack-ios.xcworkspace` for this project from now on. |
|||
Sending stats |
|||
Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed. |
|||
|
|||
[!] Automatically assigning platform `ios` with version `11.3` on target `hello-blockstack-ios` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`. |
|||
``` |
|||
|
|||
This command creates a number of files |
|||
|
|||
4. Review the files that the `pod` installation created: |
|||
|
|||
```bash |
|||
$ ls |
|||
Podfile hello-blockstack-ios hello-blockstack-iosTests |
|||
Podfile.lock hello-blockstack-ios.xcodeproj |
|||
Pods hello-blockstack-ios.xcworkspace |
|||
``` |
|||
|
|||
5. Start XCode and choose **Open another project**. |
|||
6. Choose the `.xcworkspace` file created in your project folder. |
|||
|
|||
![](images/open-xcworkspace.png) |
|||
|
|||
When you open the workspace, the system will begin indexing the project. Then, after indexing, you **may** see a warning indicator at the top in the |
|||
project title. If you see the warning, continue to step 7. Otherwise, go to the next section. |
|||
|
|||
7. Click the signal to reveal the warning. |
|||
8. Click **Next**. |
|||
|
|||
![](images/indicator.png) |
|||
|
|||
9. Select all the targets and press **Next** and **Continue** when prompted. |
|||
|
|||
Make sure you have no errors; warnings are acceptable to continue. |
|||
|
|||
### Choose a custom protocol handler |
|||
|
|||
You'll need to choose a custom protocol handler that is unique to your app. This |
|||
is so that your app's web-based authentication `redirect.html` endpoint can redirect |
|||
the user back to your iOS app. In this example, you use `myblockstackapp://`. |
|||
|
|||
1. Open the `.xcworkspace` file in XCode if it isn't open already. |
|||
2. Select the top node of your project. |
|||
3. Select the **Info** tab in XCode. |
|||
4. Scroll to **URL Types** and press **+** (plus) sign. |
|||
5. Enter an **Identifier** and **URL Schemes** value. |
|||
|
|||
| **Identifier** | `MyBlockstackApp` | |
|||
| **URL Schemes** | `myblockstackapp` | |
|||
|
|||
6. Set the **Role** to **Editor**. |
|||
|
|||
When you are done the **URL Types** appears as follows: |
|||
|
|||
![](images/url-type.png) |
|||
|
|||
### Add a splash screen |
|||
|
|||
All iOS applications require a splash page. |
|||
|
|||
1. Select `Assets.xcassets` |
|||
2. Move your cursor into the area below Appicon. |
|||
3. Right click and choose **New Image Set** |
|||
|
|||
![](images/image-set-0.png) |
|||
|
|||
4. Download the Blockstack icon. |
|||
|
|||
![](images/blockstack-icon.png) |
|||
|
|||
5. Drag the downloaded file into the **3X** position in your new Images folder. |
|||
|
|||
![](images/image-set-1.png) |
|||
|
|||
6. Select the `LaunchScreen.storyboard`. |
|||
7. Choose **Open As > Source Code**. |
|||
|
|||
![](images/open-as.png) |
|||
|
|||
8. Replace the content of the `<scenes>` element with the following: |
|||
|
|||
``` |
|||
<scenes> |
|||
<!--View Controller--> |
|||
<scene sceneID="EHf-IW-A2E"> |
|||
<objects> |
|||
<viewController id="01J-lp-oVM" sceneMemberID="viewController"> |
|||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> |
|||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> |
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
|||
<subviews> |
|||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Image" translatesAutoresizingMaskIntoConstraints="NO" id="SpU-hA-y2f"> |
|||
<rect key="frame" x="155.5" y="273" width="64" height="64"/> |
|||
</imageView> |
|||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hello Blockstack iOS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wfj-A6-BZM"> |
|||
<rect key="frame" x="108" y="432" width="158" height="21"/> |
|||
<fontDescription key="fontDescription" type="system" pointSize="17"/> |
|||
<nil key="textColor"/> |
|||
<nil key="highlightedColor"/> |
|||
</label> |
|||
</subviews> |
|||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
|||
<constraints> |
|||
<constraint firstItem="Wfj-A6-BZM" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="AZy-qf-xHq"/> |
|||
<constraint firstItem="Wfj-A6-BZM" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="412" id="SwP-qV-1RP"/> |
|||
<constraint firstItem="SpU-hA-y2f" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="XdI-Db-fDo"/> |
|||
<constraint firstItem="SpU-hA-y2f" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="253" id="xc5-po-W1E"/> |
|||
</constraints> |
|||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> |
|||
</view> |
|||
</viewController> |
|||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> |
|||
</objects> |
|||
<point key="canvasLocation" x="52" y="374.66266866566718"/> |
|||
</scene> |
|||
</scenes> |
|||
``` |
|||
|
|||
9. Immediately after scenes but before the close of the `</document>` tag add the following `<resources>`. |
|||
|
|||
```xml |
|||
> |
|||
<image name="Image" width="64" height="64"/> |
|||
> |
|||
> |
|||
``` |
|||
|
|||
10. Choose **Run > Run app** in the emulator. |
|||
|
|||
The emulator now contains a new splash screen. |
|||
|
|||
![](images/splash.png) |
|||
|
|||
### Update the Main.storyboard |
|||
|
|||
Rather than have you build up your own UI in the interface builder, this section has you copy and paste a layout into the XML file source code for the **Main.storyboard** file. |
|||
|
|||
1. Select the `Main.storyboard` file. |
|||
2. Choose **Open As > Source Code** |
|||
|
|||
The `hello-blockstack-ios/Base.lproj/Main.storyboard` file |
|||
defines the graphical elements, and their functionality will be defined in |
|||
a respective `.swift` file. |
|||
|
|||
3. Within the `<viewController>` element, replace the existing `<view>` sub-element with the following: |
|||
|
|||
```xml |
|||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> |
|||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> |
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
|||
<subviews> |
|||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="hello-blockstack-ios" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9eE-ZS-LU9"> |
|||
<rect key="frame" x="0.0" y="101" width="375" height="50"/> |
|||
<color key="backgroundColor" red="0.44735813140000003" green="0.1280144453" blue="0.57268613580000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
|||
<constraints> |
|||
<constraint firstAttribute="height" constant="50" id="U5v-13-4Ux"/> |
|||
</constraints> |
|||
<fontDescription key="fontDescription" type="system" pointSize="17"/> |
|||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
|||
<nil key="highlightedColor"/> |
|||
</label> |
|||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lfp-KX-BDb"> |
|||
<rect key="frame" x="100" y="382" width="175" height="40"/> |
|||
<color key="backgroundColor" red="0.1215686275" green="0.12941176469999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
|||
<constraints> |
|||
<constraint firstAttribute="height" constant="40" id="8fN-Ro-Krn"/> |
|||
</constraints> |
|||
<color key="tintColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/> |
|||
<state key="normal" title="Sign into Blocksack"> |
|||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
|||
</state> |
|||
<connections> |
|||
<action selector="signIn:" destination="BYZ-38-t0r" eventType="touchUpInside" id="nV7-rt-euZ"/> |
|||
</connections> |
|||
</button> |
|||
</subviews> |
|||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
|||
<constraints> |
|||
<constraint firstItem="9eE-ZS-LU9" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="2ZP-tU-h9Y"/> |
|||
<constraint firstItem="9eE-ZS-LU9" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="81" id="DBh-q0-pAV"/> |
|||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Lfp-KX-BDb" secondAttribute="trailing" constant="100" id="MHO-ew-4Bd"/> |
|||
<constraint firstItem="Lfp-KX-BDb" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="100" id="Rsm-LP-ya7"/> |
|||
<constraint firstItem="Lfp-KX-BDb" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="362" id="chE-B7-ya6"/> |
|||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="9eE-ZS-LU9" secondAttribute="trailing" id="j0x-8j-04u"/> |
|||
</constraints> |
|||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> |
|||
</view> |
|||
` |
|||
``` |
|||
|
|||
4. Select the Main.storyboard and choose **Open as > Interface Builder - Storyboard**. |
|||
|
|||
![](images/main-storyboard.png) |
|||
|
|||
You should see the following: |
|||
|
|||
![](images/new-storyboard.png) |
|||
|
|||
### Add the UI variables to the ViewController file. |
|||
|
|||
In this section, you edit the `ViewController.swift` file using the storyboard as a starting point. |
|||
|
|||
1. Select the **Main.storyboard** and choose **Open As > Interface Builder - storyboard**. |
|||
|
|||
2. With the interface builder open, choose **Editor > Assistant** to edit the `ViewController.swift` file. |
|||
|
|||
![](images/view-editors.png) |
|||
|
|||
3. In the storyboard, select the **Sign into Blockstack** button. |
|||
|
|||
4. Control-drag from the button to the code display in the editor on the right, stopping the drag at the line below controller's `viewDidLoad()` statement. |
|||
|
|||
![](images/add-action.gif) |
|||
|
|||
5. Set the **Connection** to `Outlet`. |
|||
6. Name the new section `signInButton`. |
|||
|
|||
![](images/signinbutton.png) |
|||
|
|||
7. Press **Connect**. |
|||
8. Repeat with purple **hello-blockstack-ios** label, name it `nameLabel. |
|||
|
|||
9. Add an import statement for the `Blockstack` and another for the `SafariServices` module. |
|||
|
|||
When you are done, your `ViewController.swift` file contains the following: |
|||
|
|||
```swift |
|||
import UIKit |
|||
import Blockstack |
|||
import SafariServices |
|||
|
|||
class ViewController: UIViewController { |
|||
|
|||
override func viewDidLoad() { |
|||
super.viewDidLoad() |
|||
// Do any additional setup after loading the view. |
|||
} |
|||
|
|||
@IBOutlet weak var nameLabel: UILabel! |
|||
@IBOutlet var signInButton: UIButton! |
|||
|
|||
} |
|||
``` |
|||
|
|||
And XCode has added two outlines to the `Main.storyboard` source. |
|||
|
|||
```xml |
|||
<connections> |
|||
<outlet property="nameLabel" destination="9eE-ZS-LU9" id="Ahv-Te-ZZo"/> |
|||
<outlet property="signInButton" destination="Lfp-KX-BDb" id="yef-Jj-uPt"/> |
|||
</connections> |
|||
``` |
|||
|
|||
Your connectors will have their own `destination` and `id` values. |
|||
|
|||
### Edit the ViewController.swift file |
|||
|
|||
Now, you are ready to connect your application with your Blockstack Web |
|||
Application. Normally, after building your Web application you would have |
|||
registred it with Blockstack and the app would be available on the Web. This |
|||
example skips this registration step and uses an example application we've |
|||
already created for you: |
|||
|
|||
`https://heuristic-brown-7a88f8.netlify.app/redirect.html` |
|||
|
|||
This web application already has a redirect in place for you. You'll reference |
|||
this application in your mobile add for now. In XCode, do the following; |
|||
|
|||
1. Open the `ViewController.swift` file. |
|||
2. Add an import both for `Blockstack`. |
|||
|
|||
``` |
|||
t |
|||
k |
|||
` |
|||
|
|||
``` |
|||
|
|||
3. Add a private `updateUI()` function. |
|||
|
|||
This function takes care of loading the user data from Blockstack. |
|||
|
|||
```swift |
|||
private func updateUI() { |
|||
DispatchQueue.main.async { |
|||
if Blockstack.shared.isUserSignedIn() { |
|||
// Read user profile data |
|||
let retrievedUserData = Blockstack.shared.loadUserData() |
|||
print(retrievedUserData?.profile?.name as Any) |
|||
let name = retrievedUserData?.profile?.name ?? "Nameless Person" |
|||
self.nameLabel?.text = "Hello, \(name)" |
|||
self.nameLabel?.isHidden = false |
|||
self.signInButton?.setTitle("Sign Out", for: .normal) |
|||
print("UI update SIGNED_IN") |
|||
} else { |
|||
self.nameLabel?.text = "hello-blockstack-ios" |
|||
self.signInButton?.setTitle("Sign into Blockstack", for: .normal) |
|||
print("UI update SIGNED_OUT") |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The function uses the `Blockstack.shared.isUserSignedIn()` method to determine if |
|||
the user is already logged into Blockstack or not. It then uses the |
|||
`Blockstack.shared.loadUserData()` method to load the user data and update |
|||
the application display with the username. |
|||
|
|||
4. Replace the content of the `viewDidLoad()` function so that it calls this private function. |
|||
|
|||
```swift |
|||
override func viewDidLoad() { |
|||
super.viewDidLoad() |
|||
// Do any additional setup after loading the view, typically from a nib. |
|||
self.updateUI() |
|||
} |
|||
` |
|||
|
|||
``` |
|||
|
|||
5. Create a `signIn()` function that handles both sign in and out. |
|||
|
|||
The function uses the `Blockstack.shared.signIn()` and |
|||
`Blockstack.shared.signUserOut()` methods to sign the user into the application. |
|||
|
|||
```swift |
|||
@IBAction func signIn(_ sender: UIButton) { |
|||
if Blockstack.shared.isUserSignedIn() { |
|||
print("Currently signed in so signing out.") |
|||
Blockstack.shared.signUserOut() |
|||
self.updateUI() |
|||
} else { |
|||
print("Currently signed out so signing in.") |
|||
// Address of deployed example web app |
|||
Blockstack.shared.signIn(redirectURI: URL(string: "https://heuristic-brown-7a88f8.netlify.app/redirect.html")!, |
|||
appDomain: URL(string: "https://heuristic-brown-7a88f8.netlify.app")!) { authResult in |
|||
switch authResult { |
|||
case .success(let userData): |
|||
print("Sign in SUCCESS", userData.profile?.name as Any) |
|||
self.updateUI() |
|||
case .cancelled: |
|||
print("Sign in CANCELLED") |
|||
case .failed(let error): |
|||
print("Sign in FAILED, error: ", error ?? "n/a") |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
``` |
|||
|
|||
## Troubleshooting your build in XCode |
|||
|
|||
XCode builds can retain old data. To ensure your builds are clean, try the following: |
|||
|
|||
1. Reset the simulator by choosing **Hardware -> Erase all content and settings** from the menu. |
|||
2. In XCode, clean the project by choosing **Product > Clean** from the menu or press `Command + Shift + K` on your keyboard. |
|||
3. Clean the build folder by pressing `Command + Option + Shift + K` on your keyboard. |
|||
4. Run the code on the simulator again. |
@ -0,0 +1,6 @@ |
|||
--- |
|||
title: Mine Stacks Token |
|||
description: Run a node, earn STX, and learn how Proof of Transfer (PoX) works |
|||
--- |
|||
|
|||
# Mine Stacks Token |
@ -1,6 +0,0 @@ |
|||
--- |
|||
title: Blockstack Technical Whitepaper |
|||
layout: externalurl |
|||
|
|||
redirect_url: https://blockstack.org/whitepaper.pdf |
|||
--- |
@ -1,3 +1,9 @@ |
|||
--- |
|||
title: Best practices |
|||
--- |
|||
|
|||
# Best practices |
|||
|
|||
## Hardware and OS requirements |
|||
|
|||
- A 64-bit CPU running at at least 1 GHz is _highly_ recommended (but not strictly required) |
@ -0,0 +1,5 @@ |
|||
--- |
|||
title: Overview |
|||
--- |
|||
|
|||
# Overview |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue