1598286180
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'
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.
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
1651383480
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
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`
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 pairsgetAttMap
takes a mapping of resource name to attribute/values pairsimportValueMap
takes a mapping of import name to values pairsExample:
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
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'
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:
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:
Implemented by this plugin
#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"]
}
#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"]
}
{
"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"]
}
#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)
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
1649042880
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.
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. WebView
s 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.
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);
This is your configuration object for the client. The config is passed into each of the methods with optional overrides.
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.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.string
) REQUIRED fully formed url to the OAuth authorization endpointstring
) REQUIRED fully formed url to the OAuth token exchange endpointstring
) 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.string
) fully formed url to your OAuth/OpenID Connect registration endpoint. Only necessary for servers that require client registration.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.string
) REQUIRED your client id on the auth serverstring
) client secret to pass to token exchange requests. :warning: Read more about client secretsstring
) REQUIRED the url that links back to your app with the auth codearray<string>
) the scopes for your token, e.g. ['email', 'offline_access']
.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.string
) ANDROID Client Authentication Method. Can be either basic
(default) for Basic Authentication or post
for HTTP POST body Authenticationboolean
) 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.object
) ANDROID you can specify custom headers to pass during authorize request and/or token request.{ [key: string]: value }
) headers to be passed during authorization request.{ [key: string]: value }
) headers to be passed during token retrieval request.{ [key: string]: value }
) headers to be passed during registration request.{ [key: string]: value }
) IOS you can specify additional headers to be passed for all authorize, refresh, and register requests.boolean
) (default: true) optionally allows not sending the nonce parameter, to support non-compliant providersboolean
) (default: true) optionally allows not sending the code_challenge parameter and skipping PKCE code verification, to support non-compliant providers.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)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.This is the result from the auth server:
string
) the access tokenstring
) the token expiration dateObject
) additional url parameters from the authorizationEndpoint response.Object
) additional url parameters from the tokenEndpoint response.string
) the id tokenstring
) the refresh tokenstring
) the token type, e.g. Bearerstring
]) the scopes the user has agreed to be grantedstring
) the authorization code (only if skipCodeExchange=true
)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);
string
) same as in authorization configobject
) same as in authorization configarray<string>
) REQUIRED specifies all of the redirect urls that your client will use for authenticationarray<string>
) an array that specifies which OAuth 2.0 response types your client will use. The default value is ['code']
array<string>
) an array that specifies which OAuth 2.0 grant types your client will use. The default value is ['authorization_code']
string
) requests a specific subject type for your clientstring
) specifies which clientAuthMethod
your client will use for authentication. The default value is 'client_secret_basic'
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.boolean
) ANDROID same as in authorization configobject
) ANDROID same as in authorization confignumber
) 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.This is the result from the auth server
string
) the assigned client idstring
) OPTIONAL date string of when the client id was issuedstring
) OPTIONAL the assigned client secretstring
) 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 expiresstring
) OPTIONAL uri that can be used to perform subsequent operations on the registrationstring
) token that can be used at the endpoint given by registrationClientUri
to perform subsequent operations on the registration. Will be provided if registrationClientUri
is providednpm install react-native-app-auth --save
To setup the iOS project, you need to perform three steps:
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.
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:
AppAuth.xcodeproj
to your Workspace.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;
+ }
+ }
+ }
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:
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
}
}
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.
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);
}
Values are in the code
field of the rejected Error object.
service_configuration_fetch_error
- could not fetch the service configurationauthentication_failed
- user authentication failedtoken_refresh_failed
- could not exchange the refresh token for a new JWTregistration_failed
- could not registerbrowser_not_found
(Android only) - no suitable browser installedSome 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.
Recommendations on secure token storage can be found here.
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.
These providers are OpenID compliant, which means you can use autodiscovery.
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
1655550000
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
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:
You will need LLVM 3.0 or later in order to build PNChart.
CocoaPods is the recommended way to add PNChart to your project.
pod 'PNChart'
pod install
.#import "PNChart.h"
.#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 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 have been added to PNChart for Line Chart.
lineChart.showYGridLines = YES;
lineChart.yGridLinesColor = [UIColor grayColor];
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)];
}
#import "PNChart.h"
//For LineChart
lineChart.delegate = self;
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
1659371220
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.
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!
PureLayout
to your Podfile.pod 'PureLayout'
pod install
from Terminal, then open your app's .xcworkspace
file to launch Xcode.PureLayout.h
umbrella header.use_frameworks!
in your Podfileimport PureLayout
#import <PureLayout/PureLayout.h>
(or with Modules enabled: @import PureLayout;
)use_frameworks!
in your Podfile#import "PureLayout.h"
to your bridging header.#import "PureLayout.h"
That's it - now go write some beautiful Auto Layout code!
PureLayout/PureLayout
project to your Cartfile.github "PureLayout/PureLayout"
carthage update
, then follow the additional steps required to add the framework into your project.import PureLayout
#import <PureLayout/PureLayout.h>
(or with Modules enabled: @import PureLayout;
)That's it - now go write some beautiful Auto Layout code!
PureLayout.h
header.#import "PureLayout.h"
to your bridging header.#import "PureLayout.h"
That's it - now go write some beautiful Auto Layout code!
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 are tagged in the git commit history using semantic versioning. Check out the releases and release notes for each version.
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:
auto...
, which also makes it easy for Xcode to autocomplete as you type.relation:
parameter to make the constraint an inequality.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 onlyALMarginAxis
available in iOS 8.0 and higher onlyAdditionally, 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
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.
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.
Check out some Tips and Tricks to keep in mind when using the API.
There are quite a few different ways to implement Auto Layout. Here is a quick overview of the available options:
PureLayout takes a balanced approach to Auto Layout that makes it well suited for any project.
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
1659155700
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.
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.
INTULocationManager requires iOS 9.0 or later.
INTULocationManager
to your Podfile.pod 'INTULocationManager'
pod install
from Terminal, then open your app's .xcworkspace
file to launch Xcode.INTULocationManager.h
header.use_frameworks!
in your Podfileimport INTULocationManager
#import <INTULocationManager/INTULocationManager.h>
(or with Modules enabled: @import INTULocationManager;
)use_frameworks!
in your Podfile#import "INTULocationManager.h"
to your bridging header.#import "INTULocationManager.h"
intuit/LocationManager
project to your Cartfile.github "intuit/LocationManager"
carthage update
, then follow the additional steps required to add the iOS and/or Mac frameworks into your project.import INTULocationManager
#import <INTULocationManager/INTULocationManager.h>
(or with Modules enabled: @import INTULocationManager;
)INTULocationManager.h
header.#import "INTULocationManager.h"
to your bridging header.#import "INTULocationManager.h"
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.
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.
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.
Starting with iOS 12, you will have access to set the desiredActivityType
as CLActivityTypeAirborne
.
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.
}
}
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.
}
}];
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;
}
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.
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.
}
}];
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).
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