LoginSignup
1
2

More than 5 years have passed since last update.

React NativeでURL をパースする

Last updated at Posted at 2019-03-10

経緯

React Naviveを使ったアプリで、Deep LinkやPush通知のペイロードに格納されたURLを解析したいのですが、有効な方法が見つからないのでiOS/Androidデバイスの(ネイティブ)ライブラリを使いそれらをパースするコードを実装しました。

方針

iOS, AndroidともにURLを解析するための似たAPIがあるので単純にそれらを呼び出します。
ネイティブコードを呼び出すので、この機能の戻り値はPromise経由で返すようにしています。
これらのAPIではクエリーパラメータについて、&で結合された文字列(式)だけが戻ってくるので、JS側でキーと値を抽出しています。
例外処理やエラー処理は適当です(状況に合わせて書き換えて下さい)。

完全なコード

こちらにあります。
https://github.com/flipfrog/react-native-url-parse

API

こんな感じで使用します。

import URLParse from './URLParse';
:

  async componentDidMount(): void {
    const url = await URLParse.parse(this.state.urlSpec);

    this.setState({
      protocol: url.protocol, // httpsとかのプロトコルスキーマ
      host: url.host, // ホスト名
      port: url.port, // ポート番号
      path: url.path, // パス
      query: url.query, // クエリーパラメータ
      ref: url.ref, // インデックス
      queryMap: url.queryMap, // クエリーパラメータのマップオブジェクト
    });
  }
URLParse.js
import {NativeModules} from 'react-native';

export default class URLParse {
    static async parse(urlSpec: string) {
        const url = await NativeModules.RNURLParseModule.parse(urlSpec);
        if (url && url.query) {
            const expressions = url.query.split('&');
            const queryMap = {};
            for (let expression of expressions) {
                const values = expression.split('=');
                queryMap[values[0]] = (values[1] ? values[1] : null);
            }
            url['queryMap'] =  queryMap;
        }
        return url;
    }
}

iOS

iOSは、.hと.mファイル(Objective-Cを使いました)を作成するだけです。

RNURLParseModule.h
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else
#import "RCTBridgeModule.h"
#endif

@interface RNURLParseModule : NSObject <RCTBridgeModule>
@end
RNURLParseModule.m
#import "RNURLParseModule.h"

#if __has_include("RCTUtils.h")
#import "RCTUtils.h"
#else
#import <React/RCTUtils.h>
#endif

#import <Foundation/Foundation.h>

@implementation RNURLParseModule {
}

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(parse:(NSString *)urlSpec
                  parseWithResolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  NSURL *url = [NSURL URLWithString:urlSpec];
  if (url == NULL) {
    NSDictionary *errorDic = @{
                               NSLocalizedDescriptionKey:@"Parse error",
                               NSLocalizedRecoverySuggestionErrorKey:@"Confirm parameter urlSpec."
                               };
    NSError *error = [[NSError alloc] initWithDomain:@"org.reactjs.native.example.URLParseSample.parse"
                                                code:-1 userInfo:errorDic];
    reject(@"Parse error", @"Parse error", error);
  } else {
    NSDictionary *info = @{
                           @"protocol": [url scheme],
                           @"host": [url host],
                           @"port": [url port],
                           @"path": [url path],
                           @"query": [url query],
                           @"ref": [url fragment]
                           };
    resolve(info);
  }
}
@end

Android

Androidは、モジュールとパッケージ定義を作成して、MainApplication.javaでインスタンス化したパッケージを返すようにします。

RNURLParseModule,java
package com.urlparsesample.extension;

import java.net.URL;
import com.facebook.react.bridge.Promise;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import java.util.HashMap;
import java.util.Map;

// package private class
class RNURLParseModule extends ReactContextBaseJavaModule {
    private static final String E_URL_PARSE_ERROR = "URL Parse error";

    RNURLParseModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "RNURLParseModule";
    }

    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put("IsAndroid", true);
        return constants;
    }

    @ReactMethod
    public void parse(final String urlSpec, Promise promise) {
        WritableMap info = Arguments.createMap();
        try {
            URL url = new URL(urlSpec);
            info.putString("protocol", url.getProtocol());
            info.putString("host", url.getHost());
            info.putString("path", url.getPath());
            info.putInt("port", url.getPort());
            info.putString("query", url.getQuery());
            info.putString("ref", url.getRef());
            promise.resolve(info);
        } catch (Exception e) {
            promise.reject(E_URL_PARSE_ERROR, e);
        }
    }
}
RNURLParsePackage.java
package com.urlparsesample.extension;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

public class RNURLParsePackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules (ReactApplicationContext context) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new RNURLParseModule(context));
        return modules;
    }

    // Deprecated RN 0.47
    // @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext context) {
        return Collections.emptyList();
    }
}
MainApplication.java(抜粋)

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
              new RNURLParsePackage() // ここに追加します
      );
    }

実行結果

先に記載したGitHubのコードを実行すると下記のように結果を表示します。

ScreenShot_S.png

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2