2015年8月11日 星期二

Mock completion block with OCMock

This is an example unit test using Specta, Expecta and OCMock.
Another example - WordPress iOS app unit test.

#import "Specta.h"
#define EXP_SHORTHAND
#import "Expecta.h"
#import <OCMock/OCMock.h>
#import "RoomListViewModel.h"
#import <EXTScope.h>

SpecBegin(RoomListViewModel)

describe(@"RoomListViewModel", ^{

    __block RoomListViewModel *viewModel;
    __block id home;
    __block id room;

    beforeAll(^{

    });

    beforeEach(^{
        home = OCMClassMock([HMHome class]);
        room = OCMClassMock([HMRoom class]);
        viewModel = [[RoomListViewModel alloc] initWithHome:home];
    });

    it(@"add room with name", ^{
        OCMStub([home addRoomWithName:OCMOCK_ANY completionHandler:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
            void(^ __unsafe_unretained completionHandler)(HMRoom *room, NSError *error);
            [invocation getArgument:&completionHandler atIndex:3];
            completionHandler(room, nil);
        });    
        waitUntil(^(DoneCallback done) {
            @weakify(viewModel);
            [viewModel addRoomWithName:@"test" completionHandler:^(NSError *error) {
                @strongify(viewModel);
                expect(...);
                done();
            }];
        });

    });  

    afterEach(^{
        viewModel = nil;
    });

    afterAll(^{

    });
});

SpecEnd
void(^ __unsafe_unretained completionHandler)(HMRoom *room, NSError *error);

Why declare our block __unsafe_unretained?
See http://stackoverflow.com/a/13831074/1060971, Carl Lindberg’s comment.

[invocation getArgument:&completionHandler atIndex:3];

In NSInvocation, argument index 0 is self , index 1 is cmd.
First argument of addRoomWithName is at index 2, completion block argument is at index 3.
So, assign our mock block at index 3.

2015年8月6日 星期四

Xcode6 + Swift 1.2 Podfile

Swift 1.2 + RAC3

platform :ios, "8.0"
use_frameworks!

pod "ReactiveCocoa", '3.0-RC.1'

target :MyAppTests, :exclusive => true do
  pod 'Quick', '0.3.1'
  pod 'Nimble', '1.0.0'
end

2015年7月22日 星期三

在Mac下開發MP4的相關資源

簡介

MP4只是一種container,可以想像成容器,裡面可以放mp3或aac等音樂。
關於MP4格式的spec可以在這裡下載

Library

關於MP4檔案的操作,已經有現成的library可以使用囉~
https://code.google.com/p/mp4v2/

觀察MP4格式的工具

可以藉由atomicparsley這個工具來印出MP4檔案的內部結構

brew install atomicparsley
atomicparsley /path/to/mp4 -T 1

印出來的結果像這樣

Atom ftyp @ 0 of size: 24, ends @ 24
Atom moov @ 24 of size: 569, ends @ 593
     Atom mvhd @ 32 of size: 108, ends @ 140
     Atom iods @ 140 of size: 24, ends @ 164
     Atom trak @ 164 of size: 429, ends @ 593
         Atom tkhd @ 172 of size: 92, ends @ 264
         Atom mdia @ 264 of size: 329, ends @ 593
             Atom mdhd @ 272 of size: 32, ends @ 304
             Atom hdlr @ 304 of size: 33, ends @ 337
             Atom minf @ 337 of size: 256, ends @ 593
                 Atom smhd @ 345 of size: 16, ends @ 361
                 Atom dinf @ 361 of size: 36, ends @ 397
                     Atom dref @ 369 of size: 28, ends @ 397
                         Atom url  @ 385 of size: 12, ends @ 397
                 Atom stbl @ 397 of size: 196, ends @ 593
                     Atom stsd @ 405 of size: 96, ends @ 501
                         Atom mp4a @ 421 of size: 80, ends @ 501
                             Atom esds @ 457 of size: 44, ends @ 501
                     Atom stts @ 501 of size: 24, ends @ 525
                     Atom stsz @ 525 of size: 20, ends @ 545
                     Atom stsc @ 545 of size: 28, ends @ 573
                     Atom stco @ 573 of size: 20, ends @ 593
Atom mdat @ 593 of size: 9, ends @ 602
------------------------------------------------------
Total size: 602 bytes; 22 atoms total.
Media data: 0 bytes; 602 bytes all other atoms (100.000% atom overhead).
Total free atom space: 0 bytes; 0.000% waste.
------------------------------------------------------
AtomicParsley version: 0.9.6 (utf8)
------------------------------------------------------
Movie duration: 7504.597 seconds (02:05:04.60) - 0.00* kbp/sec bitrate (*=approximate)
Low-level details. Total tracks: 1
Trk  Type  Handler                    Kind  Lang  Bytes
1    soun  [none listed]              mp4a  und   1
     0.00* kbp/s  7504.597 sec  MPEG-4 Unknown profile: 0x0    channels: (2)

這個用python寫的工具也是不錯的選擇
https://github.com/amarghosh/mp4viewer
mp4Viewer

2015年3月10日 星期二

讓Alcatraz支援Xcode 6.2

  1. 打開Finder,跳到~/Library/Application Support/Developer/Shared/Xcode/Plug-ins
  2. 右鍵點plugin,顯示套件內容
  3. 打開Info.plist
  4. 在DVTPlugInCompatibilityUUIDs的Array加入A16FF353-8441-459E-A50C-B071F53F51B7
  5. 重啟Xcode

參考:https://github.com/supermarin/Alcatraz/issues/218

2015年2月11日 星期三

支援眾多video與audio格式的MobileVLCKit

支援格式

支援的video格式https://www.videolan.org/vlc/features.php?cat=video
支援的audio格式https://www.videolan.org/vlc/features.php?cat=audio

安裝方式

By CocoaPods

pod 'MobileVLCKit'

從night build下載
http://nightlies.videolan.org/build/ios/

Build source code,使用-f參數build出包含所有arch的framework

./buildMobileVLCKit.sh -f

https://wiki.videolan.org/VLCKit/#Building_the_framework_for_iOS

Sample Code

參考範例DropIn-Player http://cl.ly/0G2Y2M1R1X3U

初始化

記得引用MobileVLCKit的原始檔要從.m改為.mm,這樣編譯才不會出錯。

// Set up a videoView by hand. You can also do that in the nib file
   videoView = [[VLCVideoView alloc] initWithFrame:[[window contentView] bounds]];
   [[window contentView] addSubview:videoView];
   [videoView setAutoresizingMask: NSViewHeightSizable|NSViewWidthSizable];

   // Init the player object
   player = [[VLCMediaPlayer alloc] initWithVideoView:videoView];
   VLCMedia *media = [VLCMedia mediaWithPath:@"/to/my/movie"];
   //設置delegate等一下可以知道歌曲有多長
   media.delegate = self;
   [player setMedia:media];
   [player play];

控制播放進度(progress)

progressValue為UISlider傳過來的值

- (void)seek:(float) progressValue
{
    [self.mediaPlayer setPosition:progressValue];
}

顯示目前播放進度

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerTimeChanged:) name:VLCMediaPlayerTimeChanged object:nil];
- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
{
    self.playbackTime = [_player.time stringValue];
}

獲取duration

如果有把player裡面的VLCMedia設置delegate的話,就會收到通知

- (void)mediaDidFinishParsing:(VLCMedia *)aMedia
{
    //更新duration
}

監控狀態

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerStateChanged:) name:VLCMediaPlayerStateChanged object:nil];
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
    VLCMediaPlayerState state;
    switch (state) {
        case VLCMediaPlayerStatePaused:
            break;

        case VLCMediaPlayerStateStopped:
            break;

        case VLCMediaPlayerStateBuffering:
            break;

        case VLCMediaPlayerStatePlaying:
            break;

        case VLCMediaPlayerStateEnded:
            break;

        case VLCMediaPlayerStateError:
            break;

        default:
            break;
    }
}