はじめに

iOSエンジニアの小菅です。

先日以下の記事とライブラリを公開しました。

はてブもいくつかついた頃に以下のコメントを頂きました!

ScreenShot

ということで、前回作ったおかげで簡単に作成できることがすぐ想像できたので作ってしまいました。

のでこのライブラリの使い方と公開までの作業手順の紹介を書いていきます。

インストール方法と使い方

README.mdにも書きましたが、頑張って英語で書いたのでこの記事では日本語で紹介したいと思います。

インストール

CocoaPodsCarthageに対応させました。

CocoaPods

# Podfile
pod "Nuke-WebP-Plugin"

bundle exec pod installで依存関係も込みでインストールされるかと思います。

Carthage

# Cartfile
github "ryokosuge/Nuke-WebP-Plugin"

carthage update --platform iOSでインストールされます。

対応OS

現状、iOSのみになっています。

まずは公開させることを目標に作ったので、省きました。

macOSなどにも今後対応させて行きたいと思っています。

ちなみにまだbundle exec pod search Nukeで調べても出てきません…。

何かしなきゃいけないのか、はたまた時間の問題なのか…

作業手順

今回は先にCarthageに対応させてからCocoaPodsのpodspecファイルを作成してdeployしました。

Carthage対応

まずは普通にプロジェクトを作成します。

その後プロジェクトのルートディレクトリにCartfileを作成して依存関係を解決していきます。

# Cartfile
github "kean/Nuke" ~> 6.0
git "https://chromium.googlesource.com/webm/libwebp" "v0.6.0"

今回はWebP対応するのでlibwebpを入れます。

その後carthage update --no-use-binariesを実行します。

> carthage update --no-use-binaries
*** Fetching Nuke
*** Checking out libwebp at "v0.6.0"
*** Checking out Nuke at "6.0"
*** xcodebuild output can be found in /var/folders/yl/6188sfqd28l1klc_nxby14d40000gn/T/carthage-xcodebuild.bIf38x.log
*** Building scheme "Nuke tvOS" in Nuke.xcodeproj
*** Building scheme "Nuke watchOS" in Nuke.xcodeproj
*** Building scheme "Nuke iOS" in Nuke.xcodeproj
*** Building scheme "Nuke macOS" in Nuke.xcodeproj

上記のようにNuke ${platform}だけがビルドされます。

libwebpはどこに行ったかと言うと

> tree -L 3 Carthage
Carthage
├── Build
│   ├── Mac
│   │   ├── Nuke.framework
│   │   └── Nuke.framework.dSYM
│   ├── iOS
│   │   ├── Nuke.framework
│   │   ├── Nuke.framework.dSYM
│   ├── tvOS
│   │   ├── Nuke.framework
│   │   └── Nuke.framework.dSYM
│   └── watchOS
│       ├── Nuke.framework
│       └── Nuke.framework.dSYM
└── Checkouts
    ├── Nuke
    │   └── ~ 省略 ~
    └── libwebp
        ├── AUTHORS
        ├── Android.mk
        ├── CMakeLists.txt
        ├── COPYING
        ├── Carthage
        ├── ChangeLog
        ├── Makefile.am
        ├── Makefile.vc
        ├── NEWS
        ├── PATENTS
        ├── README
        ├── README.mux
        ├── autogen.sh
        ├── build.gradle
        ├── cmake
        ├── configure.ac
        ├── doc
        ├── examples
        ├── extras
        ├── gradle
        ├── gradle.properties
        ├── gradlew
        ├── gradlew.bat
        ├── imageio
        ├── iosbuild.sh
        ├── m4
        ├── makefile.unix
        ├── man
        ├── src
        └── swig

というようにCarthage > Checkoutsの中に入っています。

Nuke.frameworkCarthageおきまりのcopy-frameworkのスクリプトでlinkします。

ScreenShot2

libwebpを使う

libwebpを使う為にいくつか対応しました。

ScreenShot3

まずは上記のように HEADER_SEARCH_PATHCarthage/Checkouts/libwebprecursiveで入力しました。

あとdecode処理を今回Objective-Cで書いたので、module.modulemapを作成してimportできるようにしました。

framework module NukeWebPPlugin {
    umbrella header "Nuke_WebP_Plugin.h"

    export *
    module * { export * }
}
#import <Foundation/Foundation.h>

//! Project version number for Nuke_WebP_Plugin.
FOUNDATION_EXPORT double Nuke_WebP_PluginVersionNumber;

//! Project version string for Nuke_WebP_Plugin.
FOUNDATION_EXPORT const unsigned char Nuke_WebP_PluginVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <Nuke_WebP_Plugin/PublicHeader.h>
#import "WebPImageDecoder.h"

module.modulemapのpathも設定します。

ScreenShot5

macroの用意

Nukeが他のOSに対応していたので、あとで対応できるようにmacroを組みました。

#import <TargetConditionals.h>

#ifndef WebPImageMacros_h
#define WebPImageMacros_h

#if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
#define WEBP_PLUGIN_MAC 1
#else
#define WEBP_PLUGIN_MAC 0
#endif

#if TARGET_OS_IOS || TARGET_OS_TV
#define WEBP_PLUGIN_UIKIT 1
#else
#define WEBP_PLUGIN_UIKIT 0
#endif

#if TARGET_OS_IOS
#define WEBP_PLUGIN_IOS 1
#else
#define WEBP_PLUGIN_IOS 0
#endif

#if TARGET_OS_TV
#define WEBP_PLUGIN_TV 1
#else
#define WEBP_PLUGIN_TV 0
#endif

#endif /* WebPImageMacros_h */

上記のmacroはrs/SDWebImageを参考に作りました。

#import <TargetConditionals.h>をしないとTARGET_OS_IPHONEなどに値が入らないことに1時間ほど悩みました。

ここみて解決しました。

上記のmacroを組んだ理由ですが、iOSならUIImagemacOSならNSImageを使うのでそれの分岐をできるようにしたためです。

以下のようにimport分の分岐を入れました。

#import "WebPImageMacros.h"

#if WEBP_IMAGE_MAC
    #import <AppKit/AppKit.h>
    #define Image   NSImage
#else
    #import <UIKit/UIKit.h>
    #define Image   UIImage
#endif

@interface WebPImageDecoder : NSObject
+ (nullable Image *)decodeData:(nonnull NSData *)data;
@end

PRODUCT_NAMEの変更

Carthageで生成されるフレームワークの名前を変更します。

現状Nuke-WebP-Pluginでプロジェクトを作っていますが、フレームワーク名はNukeWebPPluginにしたかったのでPRODUCT_NAMEを変えました。

carthage build && carthage archive

あとは公開したいheaderファイルをpublicにして完了です。

以下のコマンドでbuildします。

> carthage build --no-skip-current
*** xcodebuild output can be found in /var/folders/yl/6188sfqd28l1klc_nxby14d40000gn/T/carthage-xcodebuild.VUG6a2.log
*** Building scheme "Nuke iOS" in Nuke.xcodeproj
*** Building scheme "Nuke macOS" in Nuke.xcodeproj
*** Building scheme "Nuke tvOS" in Nuke.xcodeproj
*** Building scheme "Nuke watchOS" in Nuke.xcodeproj
*** Building scheme "Nuke-WebP-Plugin iOS" in Nuke-WebP-Plugin.xcodeproj
> carthage archive
*** Found Carthage/Build/iOS/NukeWebPPlugin.framework
*** Found Carthage/Build/iOS/NukeWebPPlugin.framework.dSYM
*** Found Carthage/Build/iOS/F52359E3-6369-3A8D-BB87-A68E74463A24.bcsymbolmap
*** Found Carthage/Build/iOS/03E847A8-D09B-34A9-9FD0-E0D8C28B71AA.bcsymbolmap
*** Found Carthage/Build/iOS/1B2BFE33-28E3-3A89-B4AB-1B6D53B18460.bcsymbolmap
*** Found Carthage/Build/iOS/7A60B7EC-EABB-3D05-9EE1-A38C3A87C51D.bcsymbolmap
*** Created NukeWebPPlugin.framework.zip

CocoaPodsの対応

ここまでくればこちらは案外簡単にすみます。

まずは以下のコマンドでspecファイルを作成します。

> bundle exec pod spec create [ライブラリ名]

そうすると[ライブラリ名].podspecがディレクトリに作成されるのでこれを以下のように編集します。

> cat Nuke-WebP-Plugin.podspec
Pod::Spec.new do |s|
  s.name         = "Nuke-WebP-Plugin"
  s.version      = "1.0.0"
  s.summary      = "Nuke's WebP plugin which can load and display WebP"

  s.homepage     = "https://github.com/ryokosuge/Nuke-WebP-Plugin"
  s.license      = { :type => "MIT", :file => "LICENSE" }

  s.author             = { "ryokosuge" => "ryo.kosuge@gmail.com" }
  s.social_media_url   = 'https://twitter.com/ryo_kosuge'

  s.platform     = :ios, "9.0"
  s.source       = { :git => "https://github.com/ryokosuge/Nuke-WebP-Plugin.git", :tag => "v#{s.version}" }

  s.source_files = "Source/**/*"
  s.public_header_files = "Source/**/*.h"

  s.requires_arc = true
  s.module_name = 'NukeWebPPlugin'

  s.dependency 'libwebp', '0.6.0'
  s.dependency 'Nuke', '~> 6.0'
  s.xcconfig = {
        'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src'
  }

end

CocoaPodsのライブラリ名はpodspecで指定したライブラリ名と同じにする必要があります。

上記の値の詳細は以下のドキュメントを参考にしてください。

module_nameでフレームワークの名前を変更できます。

ファイルのlintは以下のコマンドで確認できます。

> bundle exec pod lib lint Nuke-WebP-Plugin.podspec

 -> Nuke-WebP-Plugin (1.0.0)

Nuke-WebP-Plugin passed validation.

問題なければ passed validation.が表示されます。

Warningが出力された場合

Errorの場合は、フォーマットが正しくなかったりするので修正が必要です。

しかし、Warningが出力されても仕方ないものもあります。

  • sourceversionがまだgithubのreleaseにない場合

上記の場合は一旦オプションで--allow-warningsで回避するわざもありました。

ですが、Warningもできる限りなくすべきなので、多用しないようにしましょう。

Demoアプリの作成

最後にサンプリアプリのプロジェクトを作成します。

ここは適当な名前で大丈夫です。

サンプルプロジェクトを作成後、サンプルのディレクトリにPodfileを作成して以下のように記述します。

# Demo/Podfile
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!

platform :ios, '9.0'

target 'Nuke-WebP-Demo' do
    pod 'Nuke-WebP-Plugin', :path => '../'
end

ターゲット名とライブラリ名は適時書き換えてください。

サンプルアプリはライブラリで出来ることを確認できるように作成しましょう。

自分は今回WebPの表示ができれば良かったので、UIImageViewを中央に置いてwebp画像を表示する処理をするだけのアプリを作成しました。

CocoaPodsへ登録

上記の状態でまずはgit tagをうってpushします。

tagは[プロジェクト名].podspecversionの値と同じにしてください。

tagをプッシュしたら最終確認をします。

> bundle exec pod spec lint

 -> Nuke-WebP-Plugin (1.0.0)

Analyzed 1 podspec.

Nuke-WebP-Plugin.podspec passed validation.

無事 passed validationが表示されたら準備完了です。

以下のコマンドを叩いてpushしてください。

> bundle exec pod trunk push [プロジェクト名].podspec

無事反映終わったら以下のようにCocoaPods/Specレポジトリにコミットされているかと思います。

ScreenShot4

これはpushした後すぐに確認したらこうなっていました。

かなりの頻度でpushされているので不安であればcommitを見るといいかと思います。

アカウント作成

初めてpushする際にアカウント作成が必要です。

以下のコマンドで作成出来ます。

> bundle exec pod trunk register [email] "[user name]"

自分の場合は

> bundle exec pod trunk register ryo.kosuge@gmail.com "Ryo Kosuge"

これで登録終わりです。

登録内容は以下のコマンドで出来ます。

> bundle exec pod trunk me
  - Name:     Ryo Kosuge
  - Email:    ryo.kosuge@gmail.com
  - Since:    January 15th, 15:51
  - Pods:
    - NukeWebP
    - Nuke+WebP
    - Nuke-WebP-Plugin
  - Sessions:
    - January 15th, 15:51 - May 27th, 01:37. IP: 113.43.142.102

自分が作ったライブラリのdeprecateのやり方

今回Nuke-WebP-Pluginを作成したおかげで前回作ったNuke-WebPが必要なくなったのでdeprecateしました。

それのやり方とNuke-WebPの代わりにNuke-WebP-Pluginを使用するように促す方法は以下のコマンドになります。

> bundle exec pod trunk deprecate [deprecateするライブラリ名] --in-favor-of=[使って欲しいライブラリ名]

これで非推奨の設定も完了です。

まだ bundle exec pod search Nukeなどで探しても出てきませんが、CocoaPodsのページでは無事表示されました。

ScreenShot5

これで無事公開作業は終了です。

終わりに

ということで今回もNuke関連の記事を書きました。

でも今回作ってWebP対応が思いの外、軽くできることが知れて良かったです。

あと他のライブラリたちのpodspecを読むついでにたくさんのソースコードを読めたので大変勉強になりました。

特にテストは凄いなと思いました。

コードリーディングの必要さと楽しさの再確認ができたのが一番の功績かもしれないです。

Pull Requestお待ちしております!