Rachel Cole

Rachel Cole

1601602504

ES6 Module System Crash Course (import/export)

ES6 new module system with import, export syntax is going to be the future of the web as well as Node.js, so let us understand exactly how to work with it in this video.

#es6 #javascript #web-development #programming #developer

What is GEEK

Buddha Community

ES6 Module System Crash Course (import/export)
Jamel  O'Reilly

Jamel O'Reilly

1662142380

SOAPEngine: Access Web Services using A Your IOS App, Mac OS X App

This generic SOAP client allows you to access web services using a your iOS app, Mac OS X app and Apple TV app.

With this Framework you can create iPhone, iPad, Mac OS X and AppleTv apps that supports SOAP Client Protocol. This framework able executes methods at remote web services with SOAP standard protocol.

SDK 1.x vs SDK 2.x

We are in the process of releasing our next major SDK - SDK 2.x. Publishers who use SDK 1.x should continue to use it unless instructed otherwise. Please make sure you indicate the latest SDK 1.x version when using Cocoapods.

Features


  • Support both 2001 (v1.1) and 2003 (v1.2) XML schema.
  • Support array, array of structs, dictionary and sets.
  • Support for user-defined object with serialization of complex data types and array of complex data types, even embedded multilevel structures.
  • Supports ASMX Services, WCF Services (SVC) and now also the WSDL definitions.
  • Supports Basic, Digest and NTLM Authentication, WS-Security, Client side Certificate and custom security header.
  • Supports iOS Social Account to send OAuth2.0 token on the request.
  • AES256 or 3DES Encrypt/Decrypt data without SSL security.
  • An example of service and how to use it is included in source code.

Requirements for iOS


  • iOS 8.0 and later
  • Xcode 8.0 or later
  • Security.framework
  • Accounts.framework
  • Foundation.framework
  • UIKit.framework
  • libxml2.dylib

Requirements for Mac OS X


  • OS X 10.9 and later
  • Xcode 8.0 or later
  • Security.framework
  • Accounts.framework
  • Foundation.framework
  • AppKit.framework
  • Cocoa.framework
  • libxml2.dylib

Requirements for Apple TV


  • iOS 9.0 and later
  • Xcode 8.0 or later
  • Security.framework
  • Foundation.framework
  • UIKit.framework
  • libxml2.dylib

Limitations


Known issues


Swift 4: the library is currently written in Objective-C and when you import the swift library you will get build errors like this The use of Swift 3 @objc inference in Swift 4 mode is deprecated.

For silent this warning is need sets Swift 3 @objc Inference to default value in the the Build settings of target. but It's not all; the classes used to create requests must be declared with @objcMembers and NSObject, eg:

the declaration of MyClass must become :

@objcMembers class MyClass: NSObject { ... }
class MyClass { ... }

let param = MyClass()
// ...
// ...
let soap = SOAPEngine()
soap.setValue(param, forKey: "myKey")
// ...
// ...

Security for Xcode 8.x or later


From the new Xcode 8 is required an additional setting for the apps, if this setting does not exist you will see a log message like this:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

To resolve this, add few keys in info.plist, the steps are:

  1. Open info.plist file of your project.
  2. Add a Key called NSAppTransportSecurity as a Dictionary.
  3. Add a Subkey called NSAllowsArbitraryLoads as Boolean and set its value to YES as like following image.

NSAppTransportSecurity

ref link: http://stackoverflow.com/a/32631185/4069848

How to use


with Delegates :

    #import <SOAPEngine64/SOAPEngine.h>

    // standard soap service (.asmx)
    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    soap.delegate = self; // use SOAPEngineDelegate

    // each single value
    [soap setValue:@"my-value1" forKey:@"Param1"];
    [soap setIntegerValue:1234 forKey:@"Param2"];
    // service url without ?WSDL, and you can search the soapAction in the WSDL
    [soap requestURL:@"http://www.my-web.com/my-service.asmx" 
          soapAction:@"http://www.my-web.com/My-Method-name"];
 
    #pragma mark - SOAPEngine Delegates

    - (void)soapEngine:(SOAPEngine *)soapEngine didFinishLoading:(NSString *)stringXML {

            NSDictionary *result = [soapEngine dictionaryValue];
            // read data from a dataset table
            NSArray *list = [result valueForKeyPath:@"NewDataSet.Table"];
    }

with Block programming :

    #import <SOAPEngine64/SOAPEngine.h>
    
    // TODO: your user object
    MyClass myObject = [[MyClass alloc] init];
    
    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    soap.version = VERSION_WCF_1_1; // WCF service (.svc)
    
    // service url without ?WSDL, and you can search the soapAction in the WSDL
    [soap requestURL:@"http://www.my-web.com/my-service.svc"
          soapAction:@"http://www.my-web.com/my-interface/my-method"
               value:myObject
            completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
                NSLog(@"%@", dict);
            } failWithError:^(NSError *error) {
                NSLog(@"%@", error);
            }];

directly from WSDL (not recommended is slow) :

    #import <SOAPEngine64/SOAPEngine.h>
    
    // TODO: your user object
    MyClass myObject = [[MyClass alloc] init];
    
    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    
    // service url with WSDL, and operation (method name) without tempuri
    [soap requestWSDL:@"http://www.my-web.com/my-service.amsx?wsdl"
            operation:@"my-method-name"
                value:myObject
            completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {

              NSLog(@"Result: %@", dict);

            } failWithError:^(NSError *error) {

                NSLog(@"%@", error);
            }];

with Notifications :

    #import <SOAPEngine64/SOAPEngine.h>

    // TODO: your user object
    MyClass myObject = [[MyClass alloc] init];
    
    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    soap.version = VERSION_WCF_1_1; // WCF service (.svc)
        
    [[NSNotificationCenter defaultCenter] 
                addObserver:self 
                   selector:@selector(soapEngineDidFinishLoading:) 
                       name:SOAPEngineDidFinishLoadingNotification 
                     object:nil];
    
    // service url without ?WSDL, and you can search the soapAction in the WSDL
    [soap requestURL:@"http://www.my-web.com/my-service.svc" 
          soapAction:@"http://www.my-web.com/my-interface/my-method"
                 value:myObject];
    
    #pragma mark - SOAPEngine Notifications
    
    - (void)soapEngineDidFinishLoading:(NSNotification*)notification
    {
        SOAPEngine *engine = notification.object; // SOAPEngine object
        NSDictionary *result = [engine dictionaryValue];
        NSLog(@"%@", result);
    }

Synchronous request :

    #import <SOAPEngine64/SOAPEngine.h>
    
    NSError *error = nil;
    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.responseHeader = YES; // use only for non standard MS-SOAP service like PHP
    NSDictionary *dict = [soap syncRequestURL:@"http://www.my-web.com/my-service.amsx" 
                            soapAction:@"http://tempuri.org/my-method" error:&error];
    NSLog(@"error: %@, result: %@", error, dict)

Swift 3 language :

        var soap = SOAPEngine()
        soap.userAgent = "SOAPEngine"
        soap.actionNamespaceSlash = true
        soap.version = VERSION_1_1
        soap.responseHeader = true // use only for non standard MS-SOAP service
        
        soap.setValue("param-value", forKey: "param-name")
        soap.requestURL("http://www.my-web.com/my-service.asmx",
            soapAction: "http://www.my-web.com/My-Method-name",
            completeWithDictionary: { (statusCode : Int, 
                                 dict : [AnyHashable : Any]?) -> Void in
                
                var result:Dictionary = dict! as Dictionary
                print(result)
                
            }) { (error : Error?) -> Void in
                
                print(error)
        }

settings for SOAP Authentication :

    #import <SOAPEngine64/SOAPEngine.h>

    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    
    // authorization
    soap.authorizationMethod = SOAP_AUTH_BASIC; // basic auth
    soap.username = @"my-username";
    soap.password = @"my-password";
    
    // TODO: your code here...
    

settings for Social OAuth2.0 token :

    #import <SOAPEngine64/SOAPEngine.h>
    #import <Accounts/Accounts.h>

    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    
    // token authorization
    soap.authorizationMethod = SOAP_AUTH_SOCIAL;
    soap.apiKey = @"1234567890"; // your apikey https://dev.twitter.com/
    soap.socialName = ACAccountTypeIdentifierTwitter;
    
    // TODO: your code here...
    

Encryption/Decryption data without SSL/HTTPS :

    #import <SOAPEngine64/SOAPEngine.h>

    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.userAgent = @"SOAPEngine";
    soap.encryptionType = SOAP_ENCRYPT_AES256; // or SOAP_ENCRYPT_3DES
    soap.encryptionPassword = @"my-password";

    // TODO: your code here...
    

Params with Attributes :

    // book
    NSMutableDictionary *book = [NSMutableDictionary dictionaryWithObject:@"Genesis" forKey:@"name"];
    // chapter
    NSDictionary *attr = @{@"order": @"asc"};
    NSDictionary *child = [soap dictionaryForKey:@"chapter" value:@"1" attributes:attr];
    [book addEntriesFromDictionary:child]; // add chapter to book
    // book attributes
    [soap setValue:book forKey:@"Book" attributes:@{@"rack": @"2"}];

it builds a request like this:

    <Book rack="2">
        <name>Genesis</name>
        <chapter order="asc">1</chapter>
    </Book>

PAYPAL example with certificate :

    SOAPEngine *soap = [[SOAPEngine alloc] init];

    // PAYPAL associates a set of API credentials with a specific PayPal account
    // you can generate credentials from this https://developer.paypal.com/docs/classic/api/apiCredentials/
    // and convert to a p12 from terminal use :
    // openssl pkcs12 -export -in cert_key_pem.txt -inkey cert_key_pem.txt -out paypal_cert.p12
    soap.authorizationMethod = SOAP_AUTH_PAYPAL;
    soap.username = @"support_api1.your-username";
    soap.password = @"your-api-password";
    soap.clientCerficateName = @"paypal_cert.p12";
    soap.clientCertificatePassword = @"certificate-password";
    soap.responseHeader = YES;
    // use paypal for urn:ebay:api:PayPalAPI namespace
    [soap setValue:@"0" forKey:@"paypal:ReturnAllCurrencies"];
    // use paypal1 for urn:ebay:apis:eBLBaseComponents namespace
    [soap setValue:@"119.0" forKey:@"paypal1:Version"]; // ns:Version in WSDL file
    // certificate : https://api.paypal.com/2.0/ sandbox https://api.sandbox.paypal.com/2.0/
    // signature : https://api-3t.paypal.com/2.0/ sandbox https://api-3t.sandbox.paypal.com/2.0/
    [soap requestURL:@"https://api.paypal.com/2.0/"
          soapAction:@"GetBalance" completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
          
        NSLog(@"Result: %@", dict);
        
    } failWithError:^(NSError *error) {
    
        NSLog(@"%@", error);
    }];
              

Magento 2 login example :

    SOAPEngine *soap = [[SOAPEngine alloc] init];
    soap.selfSigned = YES; // only for invalid https certificates
    soap.responseHeader = YES;
    soap.actionNamespaceSlash = NO;
    soap.envelope = @"xmlns:urn=\"urn:Magento\"";
    [soap setValue:@"your-username" forKey:@"username"];
    [soap setValue:@"your-apykey" forKey:@"apiKey"];
    [soap requestURL:@"https://your-magentohost/api/v2_soap/"
          soapAction:@"urn:Mage_Api_Model_Server_V2_HandlerAction#login"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict)
    {
        NSLog(@"Login return: %@", [soap stringValue]);
        
    } failWithError:^(NSError *error) {
        
        NSLog(@"%@", error);
    }];

Upload file :

    SOAPEngine *soap = [[SOAPEngine alloc] init];

    // read local file
    NSData *data = [NSData dataWithContentsOfFile:@"my_video.mp4"];

    // send file data
    [soap setValue:data forKey:@"video"];
    [soap requestURL:@"http://www.my-web.com/my-service.asmx"
          soapAction:@"http://www.my-web.com/UploadFile"
          completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
              
              NSLog(@"Result: %@", dict);
              
          } failWithError:^(NSError *error) {
    
              NSLog(@"%@", error);
          }];
              

Download file :

    SOAPEngine *soap = [[SOAPEngine alloc] init];

    // send filename to remote webservice
    [soap setValue:"my_video.mp4" forKey:@"filename"];
    [soap requestURL:@"http://www.my-web.com/my-service.asmx"
          soapAction:@"http://www.my-web.com/DownloadFile"
          completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
            
            // local writable directory
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *filePath = [[paths firstObject] stringByAppendingPathComponent:@"my_video.mp4"];

            // the service returns file data in the tag named video
            NSData *data = dict[@"video"];
            [data writeToFile:@"my_video.mp4" atomically:YES];
              
          } failWithError:^(NSError *error) {
    
              NSLog(@"%@", error);
          }];
              

Optimizations


First of all, if you note a slowdown in the response of the request, try to change the value of the property named actionNamespaceSlash. After, when using the method named requestWSDL three steps are performed :

  1. retrieve the WSDL with an http request.
  2. processing to identify the soapAction.
  3. calls the method with an http request.

this is not optimized, very slow, instead you can use the optimization below :

  1. retrieving manually the SOAPAction directly from WSDL (once with your favorite browser).
  2. use the method named requestURL instead of requestWSDL without WSDL extension.

Install in your apps


Swift Package Manager

SOAPEngine is available as a Swift package. The repository URL is valid for adding the package in your app through the Xcode.

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

#ios #swift 

Jamel  O'Reilly

Jamel O'Reilly

1660642800

NVDSP: iOS/OSX DSP for Audio (with Novocaine)

Audio Filters on iOS and OSX

Implement high quality audio filters with just a few lines of code and Novocaine, or your own audio library of choice.

NVDSP comes with a wide variety of audio filters:

  • All Pass Filter (NVAllpassFilter)
  • Band Pass Filter, 0dB gain (NVBandpassFilter)
  • Band Pass Filter, Q gain (NVBandpassQPeakGainFilter)
  • High Pass Filter (NVHighpassFilter)
  • High Shelving Filter (NVHighShelvingFilter)
  • Low Shelving Filter (NVLowShelvingFilter)
  • Low Pass Filter (NVLowPassFilter)
  • Notch Filter (NVNotchFilter)
  • Peaking EQ Filter (NVPeakingEQFilter)

Combining it with Novocaine (highpass filter)

To start out I recommend you to get a fresh copy of Novocaine and open Novocaine's excellent example project. Then import NVDSP and the Filters folder and start your filtering journey.

// ... import Novocaine here ... 
#import "NVDSP/NVDSP.h"
#import "NVDSP/Filters/NVHighpassFilter.h"

// init Novocaine audioManager
audioManager = [Novocaine audioManager];
float samplingRate = audioManager.samplingRate;

// init fileReader which we will later fetch audio from
NSURL *inputFileURL = [[NSBundle mainBundle] URLForResource:@"Trentemoller-Miss-You" withExtension:@"mp3"];

fileReader = [[AudioFileReader alloc] 
                  initWithAudioFileURL:inputFileURL 
                  samplingRate:audioManager.samplingRate
                  numChannels:audioManager.numOutputChannels];

// setup Highpass filter
NVHighpassFilter *HPF;
HPF = [[NVHighpassFilter alloc] initWithSamplingRate:samplingRate];

HPF.cornerFrequency = 2000.0f;
HPF.Q = 0.5f;

// setup audio output block
[fileReader play];
[audioManager setOutputBlock:^(float *outData, UInt32 numFrames, UInt32 numChannels) {
    [fileReader retrieveFreshAudio:outData numFrames:numFrames numChannels:numChannels];
    
    [HPF filterData:outData numFrames:numFrames numChannels:numChannels];
}];

Note that NVDSP works with raw audio buffers, so it can also work with other libraries instead of Novocaine.

More examples

Peaking EQ filter

// import Novocaine.h and NVDSP.h
#import "NVDSP/Filter/NVPeakingEQFilter.h"
NVPeakingEQFilter *PEF = [[NVPeakingEQFilter alloc] initWithSamplingRate:audioManager.samplingRate];
PEF.centerFrequency = 1000.0f;
PEF.Q = 3.0f;
PEF.G = 20.0f;
[PEF filterData:data numFrames:numFrames numChannels:numChannels];

Lowpass filter

// import Novocaine.h and NVDSP.h
#import "NVDSP/Filter/NVLowpassFilter.h"
NVLowpassFilter *LPF = [[NVLowpassFilter alloc] initWithSamplingRate:audioManager.samplingRate];
LPF.cornerFrequency = 800.0f;
LPF.Q = 0.8f;
[LPF filterData:data numFrames:numFrames numChannels:numChannels];

Notch filter

// import Novocaine.h and NVDSP.h
#import "NVDSP/Filter/NVNotchFilter.h"
NVNotchFilter *NF = [[NVNotchFilter alloc] initWithSamplingRate:audioManager.samplingRate];
NF.centerFrequency = 3000.0f;
NF.Q = 0.8f;
[NF filterData:data numFrames:numFrames numChannels:numChannels];

Bandpass filter

There are two types of bandpass filters:

* 0 dB gain bandpass filter (NVBandpassFilter.h)
* Peak gain Q bandpass filter (NVBandpassQPeakGainFilter.h)
// import Novocaine.h and NVDSP.h
#import "NVDSP/Filter/NVBandpassFilter.h"
NVBandpassFilter *BPF = [[NVBandpassFilter alloc] initWithSamplingRate:audioManager.samplingRate];
BPF.centerFrequency = 2500.0f;
BPF.Q = 0.9f;
[BPF filterData:data numFrames:numFrames numChannels:numChannels];

Measure dB level (ranging from -51.0f to 0.0f)

// import Novocaine.h and NVDSP.h
#import "NVDSP/Utilities/NVSoundLevelMeter.h"
NVSoundLevelMeter *SLM = [[NVSoundLevelMeter alloc] init];
float dB = [SLM getdBLevel:outData numFrames:numFrames numChannels:numChannels];
NSLog(@"dB level: %f", dB);
// NSLogging in an output loop will most likely cause hickups/clicky noises, but it does log the dB level!
// To get a proper dB value, you have to call the getdBLevel method a few times (it has memory of previous values)
// You call this inside the input or outputBlock: [audioManager setOutputBlock:^...

Applying overall gain.

All sample values (typically -1.0f .. 1.0f when not clipping) are multiplied by the gain value.

// import Novocaine.h and NVDSP.h
NVDSP *generalDSP = [[NVDSP alloc] init];
[generalDSP applyGain:outData length:numFrames*numChannels gain:0.8];

Convert stereo (left/right) to mono

This converts a left and right buffer into a mono signal. It takes the average of the samples.

// Deinterleave stereo buffer into seperate left and right
float *left = (float *)malloc((numFrames + 2) * sizeof(float));
float *right = (float *)malloc((numFrames + 2) * sizeof(float));
[generalDSP deinterleave:data left:left right:right length:numFrames];

// Convert left and right to a mono 2 channel buffer
[generalDSP mono:data left:left right:right length:numFrames];

// Free buffers
free(left);
free(right);

Clipping

Multiple peaking EQs with high gains can cause clipping. Clipping is basically sample data that exceeds the maximum or minimum value of 1.0f or -1.0f respectively. Clipping will cause really loud and dirty noises, like a bad overdrive effect. You can use the method counterClipping to prevent clipping (it will reduce the sound level).

// import Novocaine.h and NVDSP.h
#import "NVDSP/Utilities/NVClippingDetection.h"
NVClippingDetection *CDT = [[NVClippingDetection alloc] init];
// ... possible clipped outData ...//
[CDT counterClipping:outData numFrames:numFrames numChannels:numChannels];
// ... outData is now safe ...//

// or get the amount of clipped samples:
 - (float) getClippedSamples:(float *)data numFrames:(UInt32)numFrames numChannels:(UInt32)numChannels;
// or get the percentage of clipped samples:
 - (float) getClippedPercentage:(float*)data numFrames:(UInt32)numFrames numChannels:(UInt32)numChannels;
// or get the maximum value of a clipped sample that was found
 - (float) getClippingSample:(float *)data numFrames:(UInt32)numFrames numChannels:(UInt32)numChannels;

Example project

See /Examples/NVDSPExample for a simple iOS XcodeProject example. Please note the Novocaine bundled with it might be outdated.

A thing to note

The NVDSP class is written in C++, so the classes that use it will have to be Objective-C++. Change all the files that use NVDSP from MyClass.m to MyClass.mm.

Author: bartolsthoorn
Source Code: https://github.com/bartolsthoorn/NVDSP
License: MIT license

#ios #swift #objective-c 

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

1662198780

AFNetworking Synchronous: Synchronous Requests for AFNetworking

AFNetworking-Synchronous

A minimal category which extends AFNetworking to support synchronous requests.

Usage

4.x

  pod 'AFNetworking', '~> 4.0'
  pod 'AFNetworking-Synchronous/4.x'
#import <AFNetworking.h>
#import <AFHTTPSessionManager+Synchronous.h>

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSError *error = nil;
NSData *result = [manager syncGET:@"/document/123"
                       parameters:paramDict
                       headers:headerDict
                             task:NULL
                            error:&error];

Your synchronous request will never return if it is dispatched on the session manager's completion queue.

You really should not perform a synchronous network request on the main thread on iOS, as it's likely to cause a crash when run outside the debugger. You probably should not on OS X either, as it's likely to cause lags in the UI.

If you must do so, create a separate queue for the completion handlers:

manager.completionQueue = dispatch_queue_create("AFNetworking+Synchronous", NULL);

3.x

  pod 'AFNetworking', '~> 3.0'
  pod 'AFNetworking-Synchronous/3.x'
#import <AFNetworking.h>
#import <AFHTTPSessionManager+Synchronous.h>

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSError *error = nil;
NSData *result = [manager syncGET:@"/document/123"
                       parameters:paramDict
                             task:NULL
                            error:&error];

Your synchronous request will never return if it is dispatched on the session manager's completion queue.

You really should not perform a synchronous network request on the main thread on iOS, as it's likely to cause a crash when run outside the debugger. You probably should not on OS X either, as it's likely to cause lags in the UI.

If you must do so, create a separate queue for the completion handlers:

manager.completionQueue = dispatch_queue_create("AFNetworking+Synchronous", NULL);

2.x

  pod 'AFNetworking', '~> 2.0'
  pod 'AFNetworking-Synchronous/2.x'
#import <AFNetworking.h>
#import <AFHTTPRequestOperationManager+Synchronous.h>

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSError *error = nil;
NSData *result = [manager syncGET:@"/document/123"
                       parameters:paramDict
                        operation:NULL
                            error:&error];

Currently there is no support for AFHTTPSessionManager.

1.x

  pod 'AFNetworking', '~> 1.0'
  pod 'AFNetworking-Synchronous/1.x'
#import <AFNetworking.h>
#import <AFHTTPRequestOperationManager+Synchronous.h>

AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:...];
NSError *error = nil;
NSData *result = [client synchronouslyGetPath:@"/document/123"
                                   parameters:paramDict
                                    operation:NULL
                                        error:&error];

Discussion

First, consider adopting an asynchronous design

Before you decide to use this category, consider whether you can adopt an asynchronous design instead. As @mattt wrote, asynchronism a tough thing to get your head around, but it's well worth the mental overhead. Rather than creating methods that fetch and return network data, use blocks or delegate methods to call back with the results when you have them.

Using the asynchronous API has many advantages:

  • When you start an operation on the main thread, you return control to the run loop immediately, so your UI can remains responsive. Blocking the main thread for a long time is never a good idea. "Be responsive," Apple urges in the OS X user experience guidelines. Asynchronous network operations allow you to do that.
  • AFNetworking makes asynchronous code easy to write and easy to read. With block-based success and failure handlers, you don't need to implement delegate protocols or provide selectors for callbacks.
  • AFNetworking and Grand Central Dispatch take care of threading for you, so your code does not need to manage threads, run selectors in the background, or invoke dispatch_async. Your completion blocks will be executed on the main thread (unless you configure the operations otherwise).
  • You can provide a better user experience while waiting for a response. Networks are unreliable, particularly for mobile users, and servers can be bogged down. Your users' experiences will be better if you design for a slow connection, which you can only do asynchronously.

However, in some cases, a synchronous response is better, such as when the document architecture or another framework is handling the multithreading for you, and expects a synchronous result. This code attempts to provide a safe and reliable way to use the framework synchronously.

While it overrides the default success and failure queues to avoid a deadlock, it can't anticipate every possible situation. In particular, you should not set the queue from which you're invoking as the processing queue, which will cause a deadlock.

The main thread

You shouldn't call these methods from the main thread. On iOS, if your application enters the background while one of these methods is running on the main thread, a deadlock may result and your application could be terminated.

AFImageRequestOperation processingBlock and custom operation subclasses

This category is suitable for most of the request operation subclasses built into AFNetworking, which process their response objects synchronously.

If you're using the processingBlock on AFImageRequestOperation, which contains essential processing in the completion handler, or your subclass performs other asynchronous processing in the completion handler, use the version in the using-completion-blocks branch.

All custom subclasses must override -responseObject. See AFHTTPRequestOperation+ResponseObject.h for more information.

Development

This project includes integration tests which use the delightful service httpbin. To run them, run pod install inside the TestProject folder, then load the workspace and execute the test action.

Author: paulmelnikow
Source Code: https://github.com/paulmelnikow/AFNetworking-Synchronous
License: MIT license

#ios #objective-c 

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