Lenora  Hauck

Lenora Hauck

1598286180

How to Set Up Import Aliases for Gatsby

In this post, we will be looking into how we can add import aliases to our gatsby site step by step.

The reason why we are setting up import aliases is more to do about readability and the look of our code when importing components from the import tree.

What I mean by that, is if we look at the following example

import Subscribe from '../../../../../../../core/modules/newsletter/mixins/Subscribe'

This just looks downright ugly in my opinion, so how can we improve it?

We can do that by updating webpack config to include aliases for our main directories that we know are going to be the base for our components.

Once we go through all the steps from this post, the end results will look like the following below

import Subscribe from '@core/modules/newsletter/mixins/Subscribe'

Adding Aliases in Gatsby

Given that Gatsby uses Webpack as its core and does not expose the config by default, we can add custom Webpack config using Gatsby’s onCreateWebpackConfig API, this will result in the custom configs being merged allowing you to modify the default webpack config.

To add the custom webpack config we need to edit (or create the file if it doesn’t exist in the root of our project) gatsby-node.js and add the new configs into it.

Configuration

const path = require("path");
exports.onCreateWebpackConfig = ({ actions }) => {
  actions.setWebpackConfig({
    resolve: {
      alias: {
        "@components": path.resolve(__dirname, "src/components"),
        "@static": path.resolve(__dirname, "static")
      }
    }
  });
}

As we can see in the above snippet, actions object give us the option to use setWebpackConfig that takes our custom webpack config and merge it to Gatsby’s webpack config.

In order to add new aliases, we are going to use webpack’s resolve alias which will allow us to use the newly created import aliased inside our components.

As you can see below, the end result after adding the new alias will look like this

import Layout from '@components/Layout';

#gatsbyjs #react #programming #webpack #javascript

What is GEEK

Buddha Community

How to Set Up Import Aliases for Gatsby
Hermann  Frami

Hermann Frami

1651383480

A Simple Wrapper Around Amplify AppSync Simulator

This serverless plugin is a wrapper for amplify-appsync-simulator made for testing AppSync APIs built with serverless-appsync-plugin.

Install

npm install serverless-appsync-simulator
# or
yarn add serverless-appsync-simulator

Usage

This plugin relies on your serverless yml file and on the serverless-offline plugin.

plugins:
  - serverless-dynamodb-local # only if you need dynamodb resolvers and you don't have an external dynamodb
  - serverless-appsync-simulator
  - serverless-offline

Note: Order is important serverless-appsync-simulator must go before serverless-offline

To start the simulator, run the following command:

sls offline start

You should see in the logs something like:

...
Serverless: AppSync endpoint: http://localhost:20002/graphql
Serverless: GraphiQl: http://localhost:20002
...

Configuration

Put options under custom.appsync-simulator in your serverless.yml file

| option | default | description | | ------------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | apiKey | 0123456789 | When using API_KEY as authentication type, the key to authenticate to the endpoint. | | port | 20002 | AppSync operations port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20002, 20012, 20022, etc.) | | wsPort | 20003 | AppSync subscriptions port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20003, 20013, 20023, etc.) | | location | . (base directory) | Location of the lambda functions handlers. | | refMap | {} | A mapping of resource resolutions for the Ref function | | getAttMap | {} | A mapping of resource resolutions for the GetAtt function | | importValueMap | {} | A mapping of resource resolutions for the ImportValue function | | functions | {} | A mapping of external functions for providing invoke url for external fucntions | | dynamoDb.endpoint | http://localhost:8000 | Dynamodb endpoint. Specify it if you're not using serverless-dynamodb-local. Otherwise, port is taken from dynamodb-local conf | | dynamoDb.region | localhost | Dynamodb region. Specify it if you're connecting to a remote Dynamodb intance. | | dynamoDb.accessKeyId | DEFAULT_ACCESS_KEY | AWS Access Key ID to access DynamoDB | | dynamoDb.secretAccessKey | DEFAULT_SECRET | AWS Secret Key to access DynamoDB | | dynamoDb.sessionToken | DEFAULT_ACCESS_TOKEEN | AWS Session Token to access DynamoDB, only if you have temporary security credentials configured on AWS | | dynamoDb.* | | You can add every configuration accepted by DynamoDB SDK | | rds.dbName | | Name of the database | | rds.dbHost | | Database host | | rds.dbDialect | | Database dialect. Possible values (mysql | postgres) | | rds.dbUsername | | Database username | | rds.dbPassword | | Database password | | rds.dbPort | | Database port | | watch | - *.graphql
- *.vtl | Array of glob patterns to watch for hot-reloading. |

Example:

custom:
  appsync-simulator:
    location: '.webpack/service' # use webpack build directory
    dynamoDb:
      endpoint: 'http://my-custom-dynamo:8000'

Hot-reloading

By default, the simulator will hot-relad when changes to *.graphql or *.vtl files are detected. Changes to *.yml files are not supported (yet? - this is a Serverless Framework limitation). You will need to restart the simulator each time you change yml files.

Hot-reloading relies on watchman. Make sure it is installed on your system.

You can change the files being watched with the watch option, which is then passed to watchman as the match expression.

e.g.

custom:
  appsync-simulator:
    watch:
      - ["match", "handlers/**/*.vtl", "wholename"] # => array is interpreted as the literal match expression
      - "*.graphql"                                 # => string like this is equivalent to `["match", "*.graphql"]`

Or you can opt-out by leaving an empty array or set the option to false

Note: Functions should not require hot-reloading, unless you are using a transpiler or a bundler (such as webpack, babel or typescript), un which case you should delegate hot-reloading to that instead.

Resource CloudFormation functions resolution

This plugin supports some resources resolution from the Ref, Fn::GetAtt and Fn::ImportValue functions in your yaml file. It also supports some other Cfn functions such as Fn::Join, Fb::Sub, etc.

Note: Under the hood, this features relies on the cfn-resolver-lib package. For more info on supported cfn functions, refer to the documentation

Basic usage

You can reference resources in your functions' environment variables (that will be accessible from your lambda functions) or datasource definitions. The plugin will automatically resolve them for you.

provider:
  environment:
    BUCKET_NAME:
      Ref: MyBucket # resolves to `my-bucket-name`

resources:
  Resources:
    MyDbTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: myTable
      ...
    MyBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: my-bucket-name
    ...

# in your appsync config
dataSources:
  - type: AMAZON_DYNAMODB
    name: dynamosource
    config:
      tableName:
        Ref: MyDbTable # resolves to `myTable`

Override (or mock) values

Sometimes, some references cannot be resolved, as they come from an Output from Cloudformation; or you might want to use mocked values in your local environment.

In those cases, you can define (or override) those values using the refMap, getAttMap and importValueMap options.

  • refMap takes a mapping of resource name to value pairs
  • getAttMap takes a mapping of resource name to attribute/values pairs
  • importValueMap takes a mapping of import name to values pairs

Example:

custom:
  appsync-simulator:
    refMap:
      # Override `MyDbTable` resolution from the previous example.
      MyDbTable: 'mock-myTable'
    getAttMap:
      # define ElasticSearchInstance DomainName
      ElasticSearchInstance:
        DomainEndpoint: 'localhost:9200'
    importValueMap:
      other-service-api-url: 'https://other.api.url.com/graphql'

# in your appsync config
dataSources:
  - type: AMAZON_ELASTICSEARCH
    name: elasticsource
    config:
      # endpoint resolves as 'http://localhost:9200'
      endpoint:
        Fn::Join:
          - ''
          - - https://
            - Fn::GetAtt:
                - ElasticSearchInstance
                - DomainEndpoint

Key-value mock notation

In some special cases you will need to use key-value mock nottation. Good example can be case when you need to include serverless stage value (${self:provider.stage}) in the import name.

This notation can be used with all mocks - refMap, getAttMap and importValueMap

provider:
  environment:
    FINISH_ACTIVITY_FUNCTION_ARN:
      Fn::ImportValue: other-service-api-${self:provider.stage}-url

custom:
  serverless-appsync-simulator:
    importValueMap:
      - key: other-service-api-${self:provider.stage}-url
        value: 'https://other.api.url.com/graphql'

Limitations

This plugin only tries to resolve the following parts of the yml tree:

  • provider.environment
  • functions[*].environment
  • custom.appSync

If you have the need of resolving others, feel free to open an issue and explain your use case.

For now, the supported resources to be automatically resovled by Ref: are:

  • DynamoDb tables
  • S3 Buckets

Feel free to open a PR or an issue to extend them as well.

External functions

When a function is not defined withing the current serverless file you can still call it by providing an invoke url which should point to a REST method. Make sure you specify "get" or "post" for the method. Default is "get", but you probably want "post".

custom:
  appsync-simulator:
    functions:
      addUser:
        url: http://localhost:3016/2015-03-31/functions/addUser/invocations
        method: post
      addPost:
        url: https://jsonplaceholder.typicode.com/posts
        method: post

Supported Resolver types

This plugin supports resolvers implemented by amplify-appsync-simulator, as well as custom resolvers.

From Aws Amplify:

  • NONE
  • AWS_LAMBDA
  • AMAZON_DYNAMODB
  • PIPELINE

Implemented by this plugin

  • AMAZON_ELASTIC_SEARCH
  • HTTP
  • RELATIONAL_DATABASE

Relational Database

Sample VTL for a create mutation

#set( $cols = [] )
#set( $vals = [] )
#foreach( $entry in $ctx.args.input.keySet() )
  #set( $regex = "([a-z])([A-Z]+)")
  #set( $replacement = "$1_$2")
  #set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
  #set( $discard = $cols.add("$toSnake") )
  #if( $util.isBoolean($ctx.args.input[$entry]) )
      #if( $ctx.args.input[$entry] )
        #set( $discard = $vals.add("1") )
      #else
        #set( $discard = $vals.add("0") )
      #end
  #else
      #set( $discard = $vals.add("'$ctx.args.input[$entry]'") )
  #end
#end
#set( $valStr = $vals.toString().replace("[","(").replace("]",")") )
#set( $colStr = $cols.toString().replace("[","(").replace("]",")") )
#if ( $valStr.substring(0, 1) != '(' )
  #set( $valStr = "($valStr)" )
#end
#if ( $colStr.substring(0, 1) != '(' )
  #set( $colStr = "($colStr)" )
#end
{
  "version": "2018-05-29",
  "statements":   ["INSERT INTO <name-of-table> $colStr VALUES $valStr", "SELECT * FROM    <name-of-table> ORDER BY id DESC LIMIT 1"]
}

Sample VTL for an update mutation

#set( $update = "" )
#set( $equals = "=" )
#foreach( $entry in $ctx.args.input.keySet() )
  #set( $cur = $ctx.args.input[$entry] )
  #set( $regex = "([a-z])([A-Z]+)")
  #set( $replacement = "$1_$2")
  #set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
  #if( $util.isBoolean($cur) )
      #if( $cur )
        #set ( $cur = "1" )
      #else
        #set ( $cur = "0" )
      #end
  #end
  #if ( $util.isNullOrEmpty($update) )
      #set($update = "$toSnake$equals'$cur'" )
  #else
      #set($update = "$update,$toSnake$equals'$cur'" )
  #end
#end
{
  "version": "2018-05-29",
  "statements":   ["UPDATE <name-of-table> SET $update WHERE id=$ctx.args.input.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.input.id"]
}

Sample resolver for delete mutation

{
  "version": "2018-05-29",
  "statements":   ["UPDATE <name-of-table> set deleted_at=NOW() WHERE id=$ctx.args.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.id"]
}

Sample mutation response VTL with support for handling AWSDateTime

#set ( $index = -1)
#set ( $result = $util.parseJson($ctx.result) )
#set ( $meta = $result.sqlStatementResults[1].columnMetadata)
#foreach ($column in $meta)
    #set ($index = $index + 1)
    #if ( $column["typeName"] == "timestamptz" )
        #set ($time = $result["sqlStatementResults"][1]["records"][0][$index]["stringValue"] )
        #set ( $nowEpochMillis = $util.time.parseFormattedToEpochMilliSeconds("$time.substring(0,19)+0000", "yyyy-MM-dd HH:mm:ssZ") )
        #set ( $isoDateTime = $util.time.epochMilliSecondsToISO8601($nowEpochMillis) )
        $util.qr( $result["sqlStatementResults"][1]["records"][0][$index].put("stringValue", "$isoDateTime") )
    #end
#end
#set ( $res = $util.parseJson($util.rds.toJsonString($util.toJson($result)))[1][0] )
#set ( $response = {} )
#foreach($mapKey in $res.keySet())
    #set ( $s = $mapKey.split("_") )
    #set ( $camelCase="" )
    #set ( $isFirst=true )
    #foreach($entry in $s)
        #if ( $isFirst )
          #set ( $first = $entry.substring(0,1) )
        #else
          #set ( $first = $entry.substring(0,1).toUpperCase() )
        #end
        #set ( $isFirst=false )
        #set ( $stringLength = $entry.length() )
        #set ( $remaining = $entry.substring(1, $stringLength) )
        #set ( $camelCase = "$camelCase$first$remaining" )
    #end
    $util.qr( $response.put("$camelCase", $res[$mapKey]) )
#end
$utils.toJson($response)

Using Variable Map

Variable map support is limited and does not differentiate numbers and strings data types, please inject them directly if needed.

Will be escaped properly: null, true, and false values.

{
  "version": "2018-05-29",
  "statements":   [
    "UPDATE <name-of-table> set deleted_at=NOW() WHERE id=:ID",
    "SELECT * FROM <name-of-table> WHERE id=:ID and unix_timestamp > $ctx.args.newerThan"
  ],
  variableMap: {
    ":ID": $ctx.args.id,
##    ":TIMESTAMP": $ctx.args.newerThan -- This will be handled as a string!!!
  }
}

Requires

Author: Serverless-appsync
Source Code: https://github.com/serverless-appsync/serverless-appsync-simulator 
License: MIT License

#serverless #sync #graphql 

React Native Bridge for AppAuth

React Native App Auth

React native bridge for AppAuth - an SDK for communicating with OAuth2 providers 

This versions supports react-native@0.63+. The last pre-0.63 compatible version is v5.1.3.

React Native bridge for AppAuth-iOS and AppAuth-Android SDKS for communicating with OAuth 2.0 and OpenID Connect providers.

This library should support any OAuth provider that implements the OAuth2 spec.

We only support the Authorization Code Flow.

Why you may want to use this library

AppAuth is a mature OAuth client implementation that follows the best practices set out in RFC 8252 - OAuth 2.0 for Native Apps including using SFAuthenticationSession and SFSafariViewController on iOS, and Custom Tabs on Android. WebViews are explicitly not supported due to the security and usability reasons explained in Section 8.12 of RFC 8252.

AppAuth also supports the PKCE ("Pixy") extension to OAuth which was created to secure authorization codes in public clients when custom URI scheme redirects are used.

To learn more, read this short introduction to OAuth and PKCE on the Formidable blog.

Supported methods

See Usage for example configurations, and the included Example application for a working sample.

authorize

This is the main function to use for authentication. Invoking this function will do the whole login flow and returns the access token, refresh token and access token expiry date when successful, or it throws an error when not successful.

import { authorize } from 'react-native-app-auth';

const config = {
  issuer: '<YOUR_ISSUER_URL>',
  clientId: '<YOUR_CLIENT_ID>',
  redirectUrl: '<YOUR_REDIRECT_URL>',
  scopes: ['<YOUR_SCOPES_ARRAY>'],
};

const result = await authorize(config);

prefetchConfiguration

ANDROID This will prefetch the authorization service configuration. Invoking this function is optional and will speed up calls to authorize. This is only supported on Android.

import { prefetchConfiguration } from 'react-native-app-auth';

const config = {
  warmAndPrefetchChrome: true,
  issuer: '<YOUR_ISSUER_URL>',
  clientId: '<YOUR_CLIENT_ID>',
  redirectUrl: '<YOUR_REDIRECT_URL>',
  scopes: ['<YOUR_SCOPES_ARRAY>'],
};

prefetchConfiguration(config);

config

This is your configuration object for the client. The config is passed into each of the methods with optional overrides.

  • issuer - (string) base URI of the authentication server. If no serviceConfiguration (below) is provided, issuer is a mandatory field, so that the configuration can be fetched from the issuer's OIDC discovery endpoint.
  • serviceConfiguration - (object) you may manually configure token exchange endpoints in cases where the issuer does not support the OIDC discovery protocol, or simply to avoid an additional round trip to fetch the configuration. If no issuer (above) is provided, the service configuration is mandatory.
    • authorizationEndpoint - (string) REQUIRED fully formed url to the OAuth authorization endpoint
    • tokenEndpoint - (string) REQUIRED fully formed url to the OAuth token exchange endpoint
    • revocationEndpoint - (string) fully formed url to the OAuth token revocation endpoint. If you want to be able to revoke a token and no issuer is specified, this field is mandatory.
    • registrationEndpoint - (string) fully formed url to your OAuth/OpenID Connect registration endpoint. Only necessary for servers that require client registration.
    • endSessionEndpoint - (string) fully formed url to your OpenID Connect end session endpoint. If you want to be able to end a user's session and no issuer is specified, this field is mandatory.
  • clientId - (string) REQUIRED your client id on the auth server
  • clientSecret - (string) client secret to pass to token exchange requests. :warning: Read more about client secrets
  • redirectUrl - (string) REQUIRED the url that links back to your app with the auth code
  • scopes - (array<string>) the scopes for your token, e.g. ['email', 'offline_access'].
  • additionalParameters - (object) additional parameters that will be passed in the authorization request. Must be string values! E.g. setting additionalParameters: { hello: 'world', foo: 'bar' } would add hello=world&foo=bar to the authorization request.
  • clientAuthMethod - (string) ANDROID Client Authentication Method. Can be either basic (default) for Basic Authentication or post for HTTP POST body Authentication
  • dangerouslyAllowInsecureHttpRequests - (boolean) ANDROID whether to allow requests over plain HTTP or with self-signed SSL certificates. :warning: Can be useful for testing against local server, should not be used in production. This setting has no effect on iOS; to enable insecure HTTP requests, add a NSExceptionAllowsInsecureHTTPLoads exception to your App Transport Security settings.
  • customHeaders - (object) ANDROID you can specify custom headers to pass during authorize request and/or token request.
    • authorize - ({ [key: string]: value }) headers to be passed during authorization request.
    • token - ({ [key: string]: value }) headers to be passed during token retrieval request.
    • register - ({ [key: string]: value }) headers to be passed during registration request.
  • additionalHeaders - ({ [key: string]: value }) IOS you can specify additional headers to be passed for all authorize, refresh, and register requests.
  • useNonce - (boolean) (default: true) optionally allows not sending the nonce parameter, to support non-compliant providers
  • usePKCE - (boolean) (default: true) optionally allows not sending the code_challenge parameter and skipping PKCE code verification, to support non-compliant providers.
  • skipCodeExchange - (boolean) (default: false) just return the authorization response, instead of automatically exchanging the authorization code. This is useful if this exchange needs to be done manually (not client-side)
  • connectionTimeoutSeconds - (number) configure the request timeout interval in seconds. This must be a positive number. The default values are 60 seconds on iOS and 15 seconds on Android.

result

This is the result from the auth server:

  • accessToken - (string) the access token
  • accessTokenExpirationDate - (string) the token expiration date
  • authorizeAdditionalParameters - (Object) additional url parameters from the authorizationEndpoint response.
  • tokenAdditionalParameters - (Object) additional url parameters from the tokenEndpoint response.
  • idToken - (string) the id token
  • refreshToken - (string) the refresh token
  • tokenType - (string) the token type, e.g. Bearer
  • scopes - ([string]) the scopes the user has agreed to be granted
  • authorizationCode - (string) the authorization code (only if skipCodeExchange=true)
  • codeVerifier - (string) the codeVerifier value used for the PKCE exchange (only if both skipCodeExchange=true and usePKCE=true)

refresh

This method will refresh the accessToken using the refreshToken. Some auth providers will also give you a new refreshToken

import { refresh } from 'react-native-app-auth';

const config = {
  issuer: '<YOUR_ISSUER_URL>',
  clientId: '<YOUR_CLIENT_ID>',
  redirectUrl: '<YOUR_REDIRECT_URL>',
  scopes: ['<YOUR_SCOPES_ARRAY>'],
};

const result = await refresh(config, {
  refreshToken: `<REFRESH_TOKEN>`,
});

revoke

This method will revoke a token. The tokenToRevoke can be either an accessToken or a refreshToken

import { revoke } from 'react-native-app-auth';

const config = {
  issuer: '<YOUR_ISSUER_URL>',
  clientId: '<YOUR_CLIENT_ID>',
  redirectUrl: '<YOUR_REDIRECT_URL>',
  scopes: ['<YOUR_SCOPES_ARRAY>'],
};

const result = await revoke(config, {
  tokenToRevoke: `<TOKEN_TO_REVOKE>`,
  includeBasicAuth: true,
  sendClientId: true,
});

logout

This method will logout a user, as per the OpenID Connect RP Initiated Logout specification. It requires an idToken, obtained after successfully authenticating with OpenID Connect, and a URL to redirect back after the logout has been performed.

import { logout } from 'react-native-app-auth';

const config = {
  issuer: '<YOUR_ISSUER_URL>',
};

const result = await logout(config, {
  idToken: '<ID_TOKEN>',
  postLogoutRedirectUrl: '<POST_LOGOUT_URL>',
});

register

This will perform dynamic client registration on the given provider. If the provider supports dynamic client registration, it will generate a clientId for you to use in subsequent calls to this library.

import { register } from 'react-native-app-auth';

const registerConfig = {
  issuer: '<YOUR_ISSUER_URL>',
  redirectUrls: ['<YOUR_REDIRECT_URL>', '<YOUR_OTHER_REDIRECT_URL>'],
};

const registerResult = await register(registerConfig);

registerConfig

  • issuer - (string) same as in authorization config
  • serviceConfiguration - (object) same as in authorization config
  • redirectUrls - (array<string>) REQUIRED specifies all of the redirect urls that your client will use for authentication
  • responseTypes - (array<string>) an array that specifies which OAuth 2.0 response types your client will use. The default value is ['code']
  • grantTypes - (array<string>) an array that specifies which OAuth 2.0 grant types your client will use. The default value is ['authorization_code']
  • subjectType - (string) requests a specific subject type for your client
  • tokenEndpointAuthMethod (string) specifies which clientAuthMethod your client will use for authentication. The default value is 'client_secret_basic'
  • additionalParameters - (object) additional parameters that will be passed in the registration request. Must be string values! E.g. setting additionalParameters: { hello: 'world', foo: 'bar' } would add hello=world&foo=bar to the authorization request.
  • dangerouslyAllowInsecureHttpRequests - (boolean) ANDROID same as in authorization config
  • customHeaders - (object) ANDROID same as in authorization config
  • connectionTimeoutSeconds - (number) configure the request timeout interval in seconds. This must be a positive number. The default values are 60 seconds on iOS and 15 seconds on Android.

registerResult

This is the result from the auth server

  • clientId - (string) the assigned client id
  • clientIdIssuedAt - (string) OPTIONAL date string of when the client id was issued
  • clientSecret - (string) OPTIONAL the assigned client secret
  • clientSecretExpiresAt - (string) date string of when the client secret expires, which will be provided if clientSecret is provided. If new Date(clientSecretExpiresAt).getTime() === 0, then the secret never expires
  • registrationClientUri - (string) OPTIONAL uri that can be used to perform subsequent operations on the registration
  • registrationAccessToken - (string) token that can be used at the endpoint given by registrationClientUri to perform subsequent operations on the registration. Will be provided if registrationClientUri is provided

Getting started

npm install react-native-app-auth --save

Setup

iOS Setup

To setup the iOS project, you need to perform three steps:

  1. Install native dependencies
  2. Register redirect URL scheme
  3. Define openURL callback in AppDelegate

Install native dependencies

This library depends on the native AppAuth-ios project. To keep the React Native library agnostic of your dependency management method, the native libraries are not distributed as part of the bridge.

AppAuth supports three options for dependency management.

  1. CocoaPods
cd ios
pod install

2.   Carthage

With Carthage, add the following line to your Cartfile:

github "openid/AppAuth-iOS" "master"

Then run carthage update --platform iOS.

Drag and drop AppAuth.framework from ios/Carthage/Build/iOS under Frameworks in Xcode.

Add a copy files build step for AppAuth.framework: open Build Phases on Xcode, add a new "Copy Files" phase, choose "Frameworks" as destination, add AppAuth.framework and ensure "Code Sign on Copy" is checked.

3.   Static Library

You can also use AppAuth-iOS as a static library. This requires linking the library and your project and including the headers. Suggested configuration:

  1. Create an XCode Workspace.
  2. Add AppAuth.xcodeproj to your Workspace.
  3. Include libAppAuth as a linked library for your target (in the "General -> Linked Framework and Libraries" section of your target).
  4. Add AppAuth-iOS/Source to your search paths of your target ("Build Settings -> "Header Search Paths").

Register redirect URL scheme

If you intend to support iOS 10 and older, you need to define the supported redirect URL schemes in your Info.plist as follows:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>com.your.app.identifier</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>io.identityserver.demo</string>
    </array>
  </dict>
</array>
  • CFBundleURLName is any globally unique string. A common practice is to use your app identifier.
  • CFBundleURLSchemes is an array of URL schemes your app needs to handle. The scheme is the beginning of your OAuth Redirect URL, up to the scheme separator (:) character. E.g. if your redirect uri is com.myapp://oauth, then the url scheme will is com.myapp.

Define openURL callback in AppDelegate

You need to retain the auth session, in order to continue the authorization flow from the redirect. Follow these steps:

RNAppAuth will call on the given app's delegate via [UIApplication sharedApplication].delegate. Furthermore, RNAppAuth expects the delegate instance to conform to the protocol RNAppAuthAuthorizationFlowManager. Make AppDelegate conform to RNAppAuthAuthorizationFlowManager with the following changes to AppDelegate.h:

+ #import "RNAppAuthAuthorizationFlowManager.h"

- @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
+ @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RNAppAuthAuthorizationFlowManager>

+ @property(nonatomic, weak)id<RNAppAuthAuthorizationFlowManagerDelegate>authorizationFlowManagerDelegate;

Add the following code to AppDelegate.m (to support iOS <= 10 and React Navigation deep linking)

+ - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *) options {
+  if ([self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:url]) {
+    return YES;
+  }
+  return [RCTLinkingManager application:app openURL:url options:options];
+ }

If you want to support universal links, add the following to AppDelegate.m under continueUserActivity

+ if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
+   if (self.authorizationFlowManagerDelegate) {
+     BOOL resumableAuth = [self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:userActivity.webpageURL];
+     if (resumableAuth) {
+       return YES;
+     }
+   }
+ }

Integration of the library with a Swift iOS project

The approach mentioned should work with Swift. In this case one should make AppDelegate conform to RNAppAuthAuthorizationFlowManager. Note that this is not tested/guaranteed by the maintainers.

Steps:

  1. swift-Bridging-Header.h should include a reference to #import "RNAppAuthAuthorizationFlowManager.h, like so:
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridge.h>
#import "RNAppAuthAuthorizationFlowManager.h" // <-- Add this header
#if DEBUG
#import <FlipperKit/FlipperClient.h>
// etc...

2.   AppDelegate.swift should implement the RNAppAuthorizationFlowManager protocol and have a handler for url deep linking. The result should look something like this:

@UIApplicationMain
class AppDelegate: UIApplicationDelegate, RNAppAuthAuthorizationFlowManager { //<-- note the additional RNAppAuthAuthorizationFlowManager protocol
  public weak var authorizationFlowManagerDelegate: RNAppAuthAuthorizationFlowManagerDelegate? // <-- this property is required by the protocol
  //"open url" delegate function for managing deep linking needs to call the resumeExternalUserAgentFlowWithURL method
  func application(
      _ app: UIApplication,
      open url: URL,
      options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
      return authorizationFlowManagerDelegate?.resumeExternalUserAgentFlowWithURL(with: url) ?? false
  }
}

Android Setup

Note: for RN >= 0.57, you will get a warning about compile being obsolete. To get rid of this warning, use patch-package to replace compile with implementation as in this PR - we're not deploying this right now, because it would break the build for RN < 57.

To setup the Android project, you need to add redirect scheme manifest placeholder:

To capture the authorization redirect, add the following property to the defaultConfig in android/app/build.gradle:

android {
  defaultConfig {
    manifestPlaceholders = [
      appAuthRedirectScheme: 'io.identityserver.demo'
    ]
  }
}

The scheme is the beginning of your OAuth Redirect URL, up to the scheme separator (:) character. E.g. if your redirect uri is com.myapp://oauth, then the url scheme will is com.myapp. The scheme must be in lowercase.

NOTE: When integrating with React Navigation deep linking, be sure to make this scheme (and the scheme in the config's redirectUrl) unique from the scheme defined in the deep linking intent-filter. E.g. if the scheme in your intent-filter is set to com.myapp, then update the above scheme/redirectUrl to be com.myapp.auth as seen here.

Usage

import { authorize } from 'react-native-app-auth';

// base config
const config = {
  issuer: '<YOUR_ISSUER_URL>',
  clientId: '<YOUR_CLIENT_ID>',
  redirectUrl: '<YOUR_REDIRECT_URL>',
  scopes: ['<YOUR_SCOPE_ARRAY>'],
};

// use the client to make the auth request and receive the authState
try {
  const result = await authorize(config);
  // result includes accessToken, accessTokenExpirationDate and refreshToken
} catch (error) {
  console.log(error);
}

Error messages

Values are in the code field of the rejected Error object.

  • OAuth Authorization error codes
  • OAuth Access Token error codes
  • OpendID Connect Registration error codes
  • service_configuration_fetch_error - could not fetch the service configuration
  • authentication_failed - user authentication failed
  • token_refresh_failed - could not exchange the refresh token for a new JWT
  • registration_failed - could not register
  • browser_not_found (Android only) - no suitable browser installed

Note about client secrets

Some authentication providers, including examples cited below, require you to provide a client secret. The authors of the AppAuth library

strongly recommend you avoid using static client secrets in your native applications whenever possible. Client secrets derived via a dynamic client registration are safe to use, but static client secrets can be easily extracted from your apps and allow others to impersonate your app and steal user data. If client secrets must be used by the OAuth2 provider you are integrating with, we strongly recommend performing the code exchange step on your backend, where the client secret can be kept hidden.

Having said this, in some cases using client secrets is unavoidable. In these cases, a clientSecret parameter can be provided to authorize/refresh calls when performing a token request.

Token Storage

Recommendations on secure token storage can be found here.

Maintenance Status

Active: Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.

Tested OpenID providers

These providers are OpenID compliant, which means you can use autodiscovery.

Tested OAuth2 providers

These providers implement the OAuth2 spec, but are not OpenID providers, which means you must configure the authorization and token endpoints yourself.

Download Details:
Author: FormidableLabs
Source Code: https://github.com/FormidableLabs/react-native-app-auth
License: MIT License

#react  #reactnative  #mobileapp #javascript #java 

Jamel  O'Reilly

Jamel O'Reilly

1655550000

PNChart: A Simple & Beautiful Chart Lib in Piner & CoinsMan for iOS

PNChart

You can also find swift version at here https://github.com/kevinzhow/PNChart-Swift

A simple and beautiful chart lib with animation used in Piner and CoinsMan for iOS

Requirements

PNChart works on iOS 7.0+ and is compatible with ARC projects. If you need support for iOS 6, use PNChart <= 0.8.1. Note that 0.8.2 supports iOS 8.0+ only, 0.8.3 and newer supports iOS 7.0+.

It depends on the following Apple frameworks, which should already be included with most Xcode templates:

  • Foundation.framework
  • UIKit.framework
  • CoreGraphics.framework
  • QuartzCore.framework

You will need LLVM 3.0 or later in order to build PNChart.

Usage

Cocoapods

CocoaPods is the recommended way to add PNChart to your project.

  1. Add a pod entry for PNChart to your Podfile pod 'PNChart'
  2. Install the pod(s) by running pod install.
  3. Include PNChart wherever you need it with #import "PNChart.h".

Copy the PNChart folder to your project

#import "PNChart.h"

//For Line Chart
PNLineChart * lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)];
[lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5"]];

// Line Chart No.1
NSArray * data01Array = @[@60.1, @160.1, @126.4, @262.2, @186.2];
PNLineChartData *data01 = [PNLineChartData new];
data01.color = PNFreshGreen;
data01.itemCount = lineChart.xLabels.count;
data01.getData = ^(NSUInteger index) {
    CGFloat yValue = [data01Array[index] floatValue];
    return [PNLineChartDataItem dataItemWithY:yValue];
};
// Line Chart No.2
NSArray * data02Array = @[@20.1, @180.1, @26.4, @202.2, @126.2];
PNLineChartData *data02 = [PNLineChartData new];
data02.color = PNTwitterColor;
data02.itemCount = lineChart.xLabels.count;
data02.getData = ^(NSUInteger index) {
    CGFloat yValue = [data02Array[index] floatValue];
    return [PNLineChartDataItem dataItemWithY:yValue];
};

lineChart.chartData = @[data01, data02];
[lineChart strokeChart];

You can choose to show smooth lines.

lineChart.showSmoothLines = YES;

You can set different colors for the same PNLineChartData item. for instance you can use "color" red for values less than 50 and use purple for values greater than 150.

lineChartData.rangeColors = @[
        [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(10, 30) color:[UIColor redColor]],
        [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(100, 200) color:[UIColor purpleColor]]
];
#import "PNChart.h"

//For BarC hart
PNBarChart * barChart = [[PNBarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)];
[barChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5"]];
[barChart setYValues:@[@1,  @10, @2, @6, @3]];
[barChart strokeChart];
#import "PNChart.h"

//For Circle Chart

PNCircleChart * circleChart = [[PNCircleChart alloc] initWithFrame:CGRectMake(0, 80.0, SCREEN_WIDTH, 100.0) total:[NSNumber numberWithInt:100] current:[NSNumber numberWithInt:60] clockwise:NO shadow:NO];
circleChart.backgroundColor = [UIColor clearColor];
[circleChart setStrokeColor:PNGreen];
[circleChart strokeChart];
# import "PNChart.h"
//For Pie Chart
NSArray *items = @[[PNPieChartDataItem dataItemWithValue:10 color:PNRed],
                           [PNPieChartDataItem dataItemWithValue:20 color:PNBlue description:@"WWDC"],
                           [PNPieChartDataItem dataItemWithValue:40 color:PNGreen description:@"GOOL I/O"],
                           ];



PNPieChart *pieChart = [[PNPieChart alloc] initWithFrame:CGRectMake(40.0, 155.0, 240.0, 240.0) items:items];
pieChart.descriptionTextColor = [UIColor whiteColor];
pieChart.descriptionTextFont  = [UIFont fontWithName:@"Avenir-Medium" size:14.0];
[pieChart strokeChart];
# import "PNChart.h"
//For Scatter Chart

PNScatterChart *scatterChart = [[PNScatterChart alloc] initWithFrame:CGRectMake(SCREEN_WIDTH /6.0 - 30, 135, 280, 200)];
[scatterChart setAxisXWithMinimumValue:20 andMaxValue:100 toTicks:6];
[scatterChart setAxisYWithMinimumValue:30 andMaxValue:50 toTicks:5];

NSArray * data01Array = [self randomSetOfObjects];
PNScatterChartData *data01 = [PNScatterChartData new];
data01.strokeColor = PNGreen;
data01.fillColor = PNFreshGreen;
data01.size = 2;
data01.itemCount = [[data01Array objectAtIndex:0] count];
data01.inflexionPointStyle = PNScatterChartPointStyleCircle;
__block NSMutableArray *XAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:0]];
__block NSMutableArray *YAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:1]];
data01.getData = ^(NSUInteger index) {
    CGFloat xValue = [[XAr1 objectAtIndex:index] floatValue];
    CGFloat yValue = [[YAr1 objectAtIndex:index] floatValue];
    return [PNScatterChartDataItem dataItemWithX:xValue AndWithY:yValue];
};

[scatterChart setup];
self.scatterChart.chartData = @[data01];
/***
this is for drawing line to compare
CGPoint start = CGPointMake(20, 35);
CGPoint end = CGPointMake(80, 45);
[scatterChart drawLineFromPoint:start ToPoint:end WithLineWith:2 AndWithColor:PNBlack];
***/
scatterChart.delegate = self;

Legend

Legend has been added to PNChart for Line and Pie Charts. Legend items position can be stacked or in series.

#import "PNChart.h"

//For Line Chart

//Add Line Titles for the Legend
data01.dataTitle = @"Alpha";
data02.dataTitle = @"Beta Beta Beta Beta";

//Build the legend
self.lineChart.legendStyle = PNLegendItemStyleSerial;
UIView *legend = [self.lineChart getLegendWithMaxWidth:320];

//Move legend to the desired position and add to view
[legend setFrame:CGRectMake(100, 400, legend.frame.size.width, legend.frame.size.height)];
[self.view addSubview:legend];


//For Pie Chart

//Build the legend
self.pieChart.legendStyle = PNLegendItemStyleStacked;
UIView *legend = [self.pieChart getLegendWithMaxWidth:200];

//Move legend to the desired position and add to view
[legend setFrame:CGRectMake(130, 350, legend.frame.size.width, legend.frame.size.height)];
[self.view addSubview:legend];

Grid Lines

Grid lines have been added to PNChart for Line Chart.

lineChart.showYGridLines = YES;
lineChart.yGridLinesColor = [UIColor grayColor];

Update Value

Now it's easy to update value in real time

if ([self.title isEqualToString:@"Line Chart"]) {

    // Line Chart #1
    NSArray * data01Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)];
    PNLineChartData *data01 = [PNLineChartData new];
    data01.color = PNFreshGreen;
    data01.itemCount = data01Array.count;
    data01.inflexionPointStyle = PNLineChartPointStyleTriangle;
    data01.getData = ^(NSUInteger index) {
        CGFloat yValue = [data01Array[index] floatValue];
        return [PNLineChartDataItem dataItemWithY:yValue];
    };

    // Line Chart #2
    NSArray * data02Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)];
    PNLineChartData *data02 = [PNLineChartData new];
    data02.color = PNTwitterColor;
    data02.itemCount = data02Array.count;
    data02.inflexionPointStyle = PNLineChartPointStyleSquare;
    data02.getData = ^(NSUInteger index) {
        CGFloat yValue = [data02Array[index] floatValue];
        return [PNLineChartDataItem dataItemWithY:yValue];
    };

    [self.lineChart setXLabels:@[@"DEC 1",@"DEC 2",@"DEC 3",@"DEC 4",@"DEC 5",@"DEC 6",@"DEC 7"]];
    [self.lineChart updateChartData:@[data01, data02]];

}
else if ([self.title isEqualToString:@"Bar Chart"])
{
    [self.barChart setXLabels:@[@"Jan 1",@"Jan 2",@"Jan 3",@"Jan 4",@"Jan 5",@"Jan 6",@"Jan 7"]];
    [self.barChart updateChartData:@[@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30)]];
}
else if ([self.title isEqualToString:@"Circle Chart"])
{
    [self.circleChart updateChartByCurrent:@(arc4random() % 100)];
}

Callback

#import "PNChart.h"

//For LineChart

lineChart.delegate = self;

Animation

Animation is enabled by default when drawing all charts. It can be disabled by setting displayAnimation = NO.

#import "PNChart.h"

//For LineChart

lineChart.displayAnimation = NO;

//For DelegateMethod


-(void)userClickedOnLineKeyPoint:(CGPoint)point lineIndex:(NSInteger)lineIndex pointIndex:(NSInteger)pointIndex{
    NSLog(@"Click Key on line %f, %f line index is %d and point index is %d",point.x, point.y,(int)lineIndex, (int)pointIndex);
}

-(void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex{
    NSLog(@"Click on line %f, %f, line index is %d",point.x, point.y, (int)lineIndex);
}

Author: kevinzhow
Source Code: https://github.com/kevinzhow/PNChart
License: MIT license

#ios #objective-c #chart 

Jamel  O'Reilly

Jamel O'Reilly

1659371220

PureLayout: The Ultimate API for iOS & OS X Auto Layout

The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. PureLayout extends UIView/NSView, NSArray, and NSLayoutConstraint with a comprehensive Auto Layout API that is modeled after Apple's own frameworks. PureLayout is a cross-platform Objective-C library that works (and looks!) great in Swift. It is fully backwards-compatible with all versions of iOS and OS X that support Auto Layout.

Writing Auto Layout code from scratch isn't easy. PureLayout provides a fully capable and developer-friendly interface for Auto Layout. It is designed for clarity and simplicity, and takes inspiration from the AutoLayout UI options available in Interface Builder while delivering far more flexibility. The API is also highly efficient, as it adds only a thin layer of third party code and is engineered for maximum performance.

Setup

Compatibility

The current release of PureLayout supports all versions of iOS and OS X since the introduction of Auto Layout on each platform, in both Swift and Objective-C, with a single codebase!

  • Xcode
    • Language Support: Swift (any version), Objective-C
    • Fully Compatible With: Xcode 7.0
    • Minimum Supported Version: Xcode 5.0
  • iOS
    • Fully Compatible With: iOS 9.0
    • Minimum Deployment Target: iOS 6.0
  • OS X
    • Fully Compatible With: OS X 10.11
    • Minimum Deployment Target: OS X 10.7

Using CocoaPods

  1. Add the pod PureLayout to your Podfile.
pod 'PureLayout'
  1. Run pod install from Terminal, then open your app's .xcworkspace file to launch Xcode.
  2. Import the PureLayout.h umbrella header.
  • With use_frameworks! in your Podfile
    • Swift: import PureLayout
    • Objective-C: #import <PureLayout/PureLayout.h> (or with Modules enabled: @import PureLayout;)
  • Without use_frameworks! in your Podfile
    • Swift: Add #import "PureLayout.h" to your bridging header.
    • Objective-C: #import "PureLayout.h"

That's it - now go write some beautiful Auto Layout code!

Using Carthage

  1. Add the PureLayout/PureLayout project to your Cartfile.
github "PureLayout/PureLayout"
  1. Run carthage update, then follow the additional steps required to add the framework into your project.
  2. Import the PureLayout framework/module.
  • Swift: import PureLayout
  • Objective-C: #import <PureLayout/PureLayout.h> (or with Modules enabled: @import PureLayout;)

That's it - now go write some beautiful Auto Layout code!

Manually from GitHub

  1. Download the source files in the PureLayout subdirectory.
  2. Add the source files to your Xcode project.
  3. Import the PureLayout.h header.
  • Swift: Add #import "PureLayout.h" to your bridging header.
  • Objective-C: #import "PureLayout.h"

That's it - now go write some beautiful Auto Layout code!

App Extensions

To use PureLayout in an App Extension, you need to do a bit of extra configuration to prevent usage of unavailable APIs. Click here for more info.

Releases

Releases are tagged in the git commit history using semantic versioning. Check out the releases and release notes for each version.

API Cheat Sheet

This is just a handy overview of the core API methods. Explore the header files for the full API, and find the complete documentation above the implementation of each method in the corresponding .m file. A couple of notes:

  • All of the public API methods are namespaced with the prefix auto..., which also makes it easy for Xcode to autocomplete as you type.
  • Methods that create constraints also automatically install (activate) the constraint(s), then return the new constraint(s) for you to optionally store for later adjustment or removal.
  • Many methods below also have a variant which includes a relation: parameter to make the constraint an inequality.

Attributes

PureLayout defines view attributes that are used to create auto layout constraints. Here is an illustration of the most common attributes.

There are 5 specific attribute types, which are used throughout most of the API:

  • ALEdge
  • ALDimension
  • ALAxis
  • ALMargin available in iOS 8.0 and higher only
  • ALMarginAxis available in iOS 8.0 and higher only

Additionally, there is one generic attribute type, ALAttribute, which is effectively a union of all the specific types. You can think of this as the "supertype" of all of the specific attribute types, which means that it is always safe to cast a specific type to the generic ALAttribute type. (Note that the reverse is not true -- casting a generic ALAttribute to a specific attribute type is unsafe!)

UIView/NSView

- autoSetContent(CompressionResistance|Hugging)PriorityForAxis:
- autoCenterInSuperview(Margins) // Margins variant iOS 8.0+ only
- autoAlignAxisToSuperview(Margin)Axis: // Margin variant iOS 8.0+ only
- autoPinEdgeToSuperview(Edge:|Margin:)(withInset:) // Margin variant iOS 8.0+ only
- autoPinEdgesToSuperview(Edges|Margins)(WithInsets:)(excludingEdge:) // Margins variant iOS 8.0+ only
- autoPinEdge:toEdge:ofView:(withOffset:)
- autoAlignAxis:toSameAxisOfView:(withOffset:|withMultiplier:)
- autoMatchDimension:toDimension:ofView:(withOffset:|withMultiplier:)
- autoSetDimension(s)ToSize:
- autoConstrainAttribute:toAttribute:ofView:(withOffset:|withMultiplier:)
- autoPinTo(Top|Bottom)LayoutGuideOfViewController:withInset: // iOS only
- autoPinEdgeToSuperviewSafeArea: // iOS 11.0+ only
- autoPinEdgeToSuperviewSafeArea:withInset: // iOS 11.0+ only

NSArray

// Arrays of Constraints
- autoInstallConstraints
- autoRemoveConstraints
- autoIdentifyConstraints: // iOS 7.0+, OS X 10.9+ only

// Arrays of Views
- autoAlignViewsToEdge:
- autoAlignViewsToAxis:
- autoMatchViewsDimension:
- autoSetViewsDimension:toSize:
- autoSetViewsDimensionsToSize:
- autoDistributeViewsAlongAxis:alignedTo:withFixedSpacing:(insetSpacing:)(matchedSizes:)
- autoDistributeViewsAlongAxis:alignedTo:withFixedSize:(insetSpacing:)

NSLayoutConstraint

+ autoCreateAndInstallConstraints:
+ autoCreateConstraintsWithoutInstalling:
+ autoSetPriority:forConstraints:
+ autoSetIdentifier:forConstraints: // iOS 7.0+, OS X 10.9+ only
- autoIdentify: // iOS 7.0+, OS X 10.9+ only
- autoInstall
- autoRemove

Usage

Sample Code (Swift)

PureLayout dramatically simplifies writing Auto Layout code. Let's take a quick look at some examples, using PureLayout from Swift.

Initialize the view using PureLayout initializer:

let view1 = UIView(forAutoLayout: ())

If you need to use a different initializer (e.g. in UIView subclass), you can also use configureForAutoLayout:

view1.configureForAutoLayout() // alternative to UIView.init(forAutoLayout: ())

Here's a constraint between two views created (and automatically activated) using PureLayout:

view1.autoPinEdge(.top, toEdge: .bottom, ofView: view2)

Without PureLayout, here's the equivalent code you'd have to write using Apple's Foundation API directly:

NSLayoutConstraint(item: view1, attribute: .top, relatedBy: .equal, toItem: view2, attribute: .bottom, multiplier: 1.0, constant: 0.0).active = true

Many APIs of PureLayout create multiple constraints for you under the hood, letting you write highly readable layout code:

// 2 constraints created & activated in one line!
logoImageView.autoCenterInSuperview()

// 4 constraints created & activated in one line!
textContentView.autoPinEdgesToSuperviewEdges(with insets: UIEdgeInsets(top: 20.0, left: 5.0, bottom: 10.0, right: 5.0))

PureLayout always returns the constraints it creates so you have full control:

let constraint = skinnyView.autoMatchDimension(.height, toDimension: .width, ofView: tallView)

PureLayout supports safearea with iOS 11.0+:

view2.autoPinEdge(toSuperviewSafeArea: .top)

PureLayout supports all Auto Layout features including inequalities, priorities, layout margins, identifiers, and much more. It's a comprehensive, developer-friendly way to use Auto Layout.

Check out the example apps below for many more demos of PureLayout in use.

Example Apps

Open the project included in the repository (requires Xcode 6 or higher). It contains iOS (Example-iOS scheme) and OS X (Example-Mac scheme) demos of the library being used in various scenarios. The demos in the iOS example app make a great introductory tutorial to PureLayout -- run each demo, review the code used to implement it, then practice by making some changes of your own to the demo code.

Each demo in the iOS example app has a Swift and Objective-C version. To compile & run the Swift demos, you must use Xcode 7.0 or higher (Swift 2.0) and choose the Example-iOS-Xcode7 scheme. When you run the example app, you can easily switch between using the Swift and Objective-C versions of the demos. To see the constraints in action while running the iOS demos, try using different device simulators, rotating the device to different orientations, as well as toggling the taller in-call status bar in the iOS Simulator.

On OS X, while running the app, press any key to cycle through the demos. You can resize the window to see the constraints in action.

Tips and Tricks

Check out some Tips and Tricks to keep in mind when using the API.

PureLayout vs. the rest

There are quite a few different ways to implement Auto Layout. Here is a quick overview of the available options:

  • Apple NSLayoutConstraint SDK API
    • Pros: Raw power
    • Cons: Extremely verbose; tedious to write; difficult to read
  • Apple Visual Format Language
    • Pros: Concise; convenient
    • Cons: Doesn't support some use cases; lacks compile-time checking and safety; must learn syntax; hard to debug
  • Apple Interface Builder
    • Pros: Visual; interactive; provides compile-time layout checking
    • Cons: Difficult for complex layouts; cannot dynamically set constraints at runtime; encourages hardcoded magic numbers; not always WYSIWYG
  • Apple NSLayoutAnchor SDK API
    • Pros: Clean, readable, and type-safe API for creating individual constraints
    • Cons: Only available in iOS 9.0 and OS X 10.11 and higher; requires manually activating each constraint; no API for creating multiple constraints at once
  • PureLayout
    • Pros: Compatible with Objective-C and Swift codebases; consistent with Cocoa API style; cross-platform API and implementation shared across iOS and OS X; fully backwards-compatible to iOS 6 & OS X 10.7; easy to use; type-safe; efficient
    • Cons: Not the most concise expression of layout code
  • High-level Auto Layout Libraries/DSLs (Cartography, SnapKit, KeepLayout)
    • Pros: Very clean, concise, and convenient
    • Cons: Unique API style is foreign to Apple's APIs; mixed compatibility with Objective-C & Swift; greater dependency on third party code

PureLayout takes a balanced approach to Auto Layout that makes it well suited for any project.

Problems, Suggestions, Pull Requests?

Please open a new Issue here if you run into a problem specific to PureLayout, have a feature request, or want to share a comment. Note that general Auto Layout questions should be asked on Stack Overflow.

Pull requests are encouraged and greatly appreciated! Please try to maintain consistency with the existing code style. If you're considering taking on significant changes or additions to the project, please communicate in advance by opening a new Issue. This allows everyone to get onboard with upcoming changes, ensures that changes align with the project's design philosophy, and avoids duplicated work.

Author: PureLayout
Source Code: https://github.com/PureLayout/PureLayout
License: View license

#ios #swift #objective-c 

Jamel  O'Reilly

Jamel O'Reilly

1659155700

LocationManager: Easily Get The Device's Current Location on iOS

INTULocationManager makes it easy to get the device's current location and is currently heading on iOS. It is an Objective-C library that also works great in Swift.

INTULocationManager provides a block-based asynchronous API to request the current location, either once or continuously. It internally manages multiple simultaneous locations and heading requests, and each one-time location request can specify its own desired accuracy level and timeout duration. INTULocationManager automatically starts location services when the first request comes in and stops the location services when all requests have been completed, while dynamically managing the power consumed by location services to reduce the impact on battery life.

What's wrong with CLLocationManager?

CLLocationManager requires you to manually detect and handle things like permissions, stale/inaccurate locations, errors, and more. CLLocationManager uses a more traditional delegate pattern instead of the modern block-based callback pattern. And while it works fine to track changes in the user's location over time (such as, for turn-by-turn navigation), it is extremely cumbersome to correctly request a single location update (such as to determine the user's current city to get a weather forecast, or to autofill an address from the current location).

INTULocationManager makes it easy to request both the device's current location, either once or continuously, as well as the device's continuous heading. The API is extremely simple for both one-time location requests and recurring subscriptions to location updates. For one-time location requests, you can specify how accurate of a location you need, and how long you're willing to wait to get it. Significant location change monitoring is also supported. INTULocationManager is power efficient and conserves the device's battery by automatically determining and using the most efficient Core Location accuracy settings, and by automatically powering down location services (e.g. GPS or compass) when they are no longer needed.

Installation

INTULocationManager requires iOS 9.0 or later.

Using CocoaPods

  1. Add the pod INTULocationManager to your Podfile.
pod 'INTULocationManager'
  1. Run pod install from Terminal, then open your app's .xcworkspace file to launch Xcode.
  2. Import the INTULocationManager.h header.
  • With use_frameworks! in your Podfile
    • Swift: import INTULocationManager
    • Objective-C: #import <INTULocationManager/INTULocationManager.h> (or with Modules enabled: @import INTULocationManager;)
  • Without use_frameworks! in your Podfile
    • Swift: Add #import "INTULocationManager.h" to your bridging header.
    • Objective-C: #import "INTULocationManager.h"

Using Carthage

  1. Add the intuit/LocationManager project to your Cartfile.
github "intuit/LocationManager"
  1. Run carthage update, then follow the additional steps required to add the iOS and/or Mac frameworks into your project.
  2. Import the INTULocationManager framework/module.
  • Swift: import INTULocationManager
  • Objective-C: #import <INTULocationManager/INTULocationManager.h> (or with Modules enabled: @import INTULocationManager;)

Manually from GitHub

  1. Download all the files in INTULocationManager subdirectory.
  2. Add the source files to your Xcode project (drag and drop is easiest).
  3. Import the INTULocationManager.h header.
  • Swift: Add #import "INTULocationManager.h" to your bridging header.
  • Objective-C: #import "INTULocationManager.h"

Usage

Requesting Permission to Access Location Services

INTULocationManager automatically handles obtaining permission to access location services when you issue a location request and the user has not already granted your app the permission to access that location services.

iOS 9 and above

Starting with iOS 8, you must provide a description for how your app uses location services by setting a string for the key NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your app's Info.plist file. INTULocationManager determines which level of permissions to request based on which description key is present. You should only request the minimum permission level that your app requires, therefore it is recommended that you use the "When In Use" level unless you require more access. If you provide values for both description keys, the more permissive "Always" level is requested.

iOS 11

Starting with iOS 11, you must provide a description for how your app uses location services by setting a string for the key NSLocationAlwaysAndWhenInUseUsageDescription in your app's Info.plist file.

iOS 12

Starting with iOS 12, you will have access to set the desiredActivityType as CLActivityTypeAirborne.

Getting the Current Location (once)

To get the device's current location, use the method requestLocationWithDesiredAccuracy:timeout:block:.

The desiredAccuracy parameter specifies how accurate and recent of a location you need. The possible values are:

INTULocationAccuracyCity          // 5000 meters or better, received within the last 10 minutes  -- lowest accuracy
INTULocationAccuracyNeighborhood  // 1000 meters or better, received within the last 5 minutes
INTULocationAccuracyBlock         // 100 meters or better, received within the last 1 minute
INTULocationAccuracyHouse         // 15 meters or better, received within the last 15 seconds
INTULocationAccuracyRoom          // 5 meters or better, received within the last 5 seconds      -- highest accuracy

The desiredActivityType parameter indicated the type of activity that is being tracked. The possible values are:

CLActivityTypeFitness               // Track fitness activities such as walking, running, cycling, and so on
CLActivityTypeAutomotiveNavigation  // Track location changes to the automobile
CLActivityTypeAirborne              // Track airborne activities - iOS 12 and above
CLActivityTypeOtherNavigation       // Track vehicular navigation that are not automobile related
CLActivityTypeOther                 // Track unknown activities. This is the default value

The timeout parameter specifies that how long you are willing to wait for a location with the accuracy you requested. The timeout guarantees that your block will execute within this period of time, either with a location of at least the accuracy you requested (INTULocationStatusSuccess), or with whichever location could be determined before the timeout interval was up (INTULocationStatusTimedOut). Pass 0.0 for no timeout (not recommended).

By default, the timeout countdown begins as soon as the requestLocationWithDesiredAccuracy:timeout:block: method is called. However, there is another variant of this method that includes a delayUntilAuthorized: parameter, which allows you to pass YES to delay the start of the timeout countdown until the user has responded to the system location services permissions prompt (if the user hasn't allowed or denied the app access yet).

Here's an example:

INTULocationManager *locMgr = [INTULocationManager sharedInstance];
[locMgr requestLocationWithDesiredAccuracy:INTULocationAccuracyCity
                                   timeout:10.0
                      delayUntilAuthorized:YES    // This parameter is optional, defaults to NO if omitted
                                     block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
                                         if (status == INTULocationStatusSuccess) {
                                             // Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
                                             // currentLocation contains the device's current location.
                                         }
                                         else if (status == INTULocationStatusTimedOut) {
                                             // Wasn't able to locate the user with the requested accuracy within the timeout interval.
                                             // However, currentLocation contains the best location available (if any) as of right now,
                                             // and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
                                         }
                                         else {
                                             // An error occurred, more info is available by looking at the specific status returned.
                                         }
                                     }];
let locationManager = INTULocationManager.sharedInstance()
locationManager.requestLocation(withDesiredAccuracy: .city,
                                            timeout: 10.0,
                               delayUntilAuthorized: true) { (currentLocation, achievedAccuracy, status) in
                                   if (status == INTULocationStatus.success) {
                                       // Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
                                       // currentLocation contains the device's current location
                                   }
                                   else if (status == INTULocationStatus.timedOut) {
                                       // Wasn't able to locate the user with the requested accuracy within the timeout interval.
                                       // However, currentLocation contains the best location available (if any) as of right now,
                                       // and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
                                   }
                                   else {
                                       // An error occurred, more info is available by looking at the specific status returned.
                                   }
           }

Subscribing to Continuous Location Updates

To subscribe to continuous location updates, use the method subscribeToLocationUpdatesWithBlock:. This method instructs location services to use the highest accuracy available (which also requires the most power). The block will execute indefinitely (even across errors, until canceled), once for every new updated location regardless of its accuracy.

If you do not need the highest possible accuracy level, you should instead use subscribeToLocationUpdatesWithDesiredAccuracy:block:. This method takes the desired accuracy level and uses it to control how much power is used by location services, with lower accuracy levels like Neighborhood and City requiring less power. Note that INTULocationManager will automatically manage the system location services accuracy level, including when there are multiple active location requests/subscriptions with different desired accuracies.

If an error occurs, the block will execute with a status other than INTULocationStatusSuccess, and the subscription will be kept alive.

Here's an example:

INTULocationManager *locMgr = [INTULocationManager sharedInstance];
[locMgr subscribeToLocationUpdatesWithDesiredAccuracy:INTULocationAccuracyHouse
                                                block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
                                                    if (status == INTULocationStatusSuccess) {
                                                        // A new updated location is available in currentLocation, and achievedAccuracy indicates how accurate this particular location is.
                                                    }
                                                    else {
                                                        // An error occurred, more info is available by looking at the specific status returned. The subscription has been kept alive.
                                                    }
                                                }];

Subscribing to Significant Location Changes

To subscribe the significant location changes, use the method subscribeToSignificantLocationChangesWithBlock:. This instructs the location services to begin monitoring for significant location changes, which is very power efficient. The block will execute indefinitely (until canceled), once for every new updated location regardless of its accuracy. Note that if there are other simultaneously active location requests or subscriptions, the block will execute for every location update (not just for significant location changes). If you intend to take action only when the location has changed significantly, you should implement custom filtering based on the distance & time received from the last location.

If an error occurs, the block will execute with a status other than INTULocationStatusSuccess, and the subscription will be kept alive.

Here's an example:

INTULocationManager *locMgr = [INTULocationManager sharedInstance];
[locMgr subscribeToSignificantLocationChangesWithBlock:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
    if (status == INTULocationStatusSuccess) {
        // A new updated location is available in currentLocation, and achievedAccuracy indicates how accurate this particular location is.
    }
    else {
        // An error occurred, more info is available by looking at the specific status returned. The subscription has been kept alive.
    }
}];

If your app has acquired the "Always" location services authorization and your app is terminated with at least one active significant location change subscription, your app may be launched in the background when the system detects a significant location change. Note that when the app terminates, all of your active location requests & subscriptions with INTULocationManager are canceled. Therefore, when the app launches due to a significant location change, you should immediately use INTULocationManager to set up a new subscription for significant location changes in order to receive the location information.

Here is an example of how to handle being launched in the background due to a significant location change:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // If you start monitoring significant location changes and your app is subsequently terminated, the system automatically relaunches the app into the background if a new event arrives.
    // Upon relaunch, you must still subscribe to significant location changes to continue receiving location events. 
    if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
        INTULocationManager *locMgr = [INTULocationManager sharedInstance];
        [locMgr subscribeToSignificantLocationChangesWithBlock:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
            // This block will be executed with the details of the significant location change that triggered the background app launch,
            // and will continue to execute for any future significant location change events as well (unless canceled).
        }];
    }
    return YES;
}

Managing Active Requests or Subscriptions

When issuing a location request, you can optionally store the request ID, which allows you to force complete or cancel the request at any time:

INTULocationManager *locMgr = [INTULocationManager sharedInstance];
INTULocationRequestID requestID = [locMgr requestLocationWithDesiredAccuracy:INTULocationAccuracyHouse
                                                                     timeout:5.0
                                                                       block:locationRequestBlock];

// Force the request to complete early, like a manual timeout (will execute the block)
[[INTULocationManager sharedInstance] forceCompleteLocationRequest:requestID];

// Cancel the request (won't execute the block)
[[INTULocationManager sharedInstance] cancelLocationRequest:requestID];

Note that subscriptions never timeout; calling forceCompleteLocationRequest: on a subscription will simply cancel it.

Subscribing to Continuous Heading Updates

To subscribe to continuous heading updates, use the method subscribeToHeadingUpdatesWithBlock:. This method does not set any default heading filter value, but you can do so using the headingFilter property on the manager instance. It also does not filter based on accuracy of the result, but rather leaves it up to you to check the returned CLHeading object's headingAccuracy property to determine whether or not it is acceptable.

The block will execute indefinitely (until canceled), once for every new updated heading regardless of its accuracy. Note that if heading requests are removed or canceled, the manager will automatically stop updating the device heading in order to preserve battery life.

If an error occurs, the block will execute with a status other than INTUHeadingStatusSuccess, and the subscription will only be automatically canceled if the device doesn't have heading support (i.e. for status INTUHeadingStatusUnavailable).

Here's an example:

INTULocationManager *locMgr = [INTULocationManager sharedInstance];
[locMgr subscribeToHeadingUpdatesWithBlock:^(CLHeading *heading, INTUHeadingStatus status) {
    if (status == INTUHeadingStatusSuccess) {
        // An updated heading is available
        NSLog(@"'Heading updates' subscription block called with Current Heading:\n%@", heading);
    } else {
        // An error occurred, more info is available by looking at the specific status returned. The subscription will be canceled only if the device doesn't have heading support.
    }
}];

Example Project

Open the project included in the repository (requires Xcode 6 and iOS 8.0 or later). It contains a LocationManagerExample scheme that will run a simple demo app. Please note that it can run in the iOS Simulator, but you need to go to the iOS Simulator's Debug > Location menu once running the app to simulate a location (the default is None).

Issues & Contributions

Please open an issue here on GitHub if you have a problem, suggestion, or other comment.

Pull requests are welcome and encouraged! There are no official guidelines, but please try to be consistent with the existing code style.

Author: intuit
Source Code: https://github.com/intuit/LocationManager
License: MIT license

#ios #swift #objective-c