diff --git a/README.md b/README.md index e4af6e9..fc21f5d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ btcbar ====== -A tiny status bar widget for OS X that displays the latest USD/BTC spot price from BitStamp, BTCe, and Coinbase. +A tiny status bar widget for OS X that displays the latest USD/BTC spot price from several USD and CNY exchanges. ## Screenshot @@ -19,14 +19,19 @@ Simply place btcbar.app in your `/Applications` folder, and optionally add it to ## Download -The current version of btcbar (2.1.4) can be downloaded here: +The current version of btcbar (2.2.0) can be downloaded here: -https://github.com/nearengine/btcbar/releases/download/v2.1.4/btcbar_2_1_4.zip +https://github.com/nearengine/btcbar/releases/download/v2.2.0/btcbar_2_2_0.zip It requires OS X 10.7+ and a 64-bit processor. ## Changelog +### 2.2.0 + +* Adds BitFinexUSD, WinkDexUSD, HuobiCNY and OKCoinCNY +* New status bar icon with Yosemite (dark theme) support + ### 2.1.4 * Removes MtGox @@ -76,4 +81,4 @@ The source is provided for inspection, considering the nature of Bitcoin. You ar If for some reason you feel like donating a few micro btc to future development, those should go here: -`1AmsBDouePjXxe2R2kFwbuCdBSpsxwtrUt` +`1D3NtjVFpoXonqk3MZwsYD9iV5WA7MRXUj` diff --git a/btcbar.xcodeproj/project.pbxproj b/btcbar.xcodeproj/project.pbxproj index cd8fb22..7b93f5d 100644 --- a/btcbar.xcodeproj/project.pbxproj +++ b/btcbar.xcodeproj/project.pbxproj @@ -11,17 +11,17 @@ 830CE09B171B6C3E00DDD525 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 830CE099171B6C3E00DDD525 /* InfoPlist.strings */; }; 830CE09D171B6C3E00DDD525 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 830CE09C171B6C3E00DDD525 /* main.m */; }; 830CE0A4171B6C3E00DDD525 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 830CE0A3171B6C3E00DDD525 /* AppDelegate.m */; }; - 838F835018602990007A7AAE /* btclogoDim.png in Resources */ = {isa = PBXBuildFile; fileRef = 838F834E18602990007A7AAE /* btclogoDim.png */; }; - 838F835118602990007A7AAE /* btclogoDim@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 838F834F18602990007A7AAE /* btclogoDim@2x.png */; }; 83C3E7491859700100FA2921 /* BitStampUSDFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3E7421859700100FA2921 /* BitStampUSDFetcher.m */; }; 83C3E74A1859700100FA2921 /* BTCeUSDFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3E7441859700100FA2921 /* BTCeUSDFetcher.m */; }; 83C3E74B1859700100FA2921 /* CoinbaseUSDFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3E7461859700100FA2921 /* CoinbaseUSDFetcher.m */; }; 83C3E74F1859702000FA2921 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E74E1859702000FA2921 /* MainMenu.xib */; }; 83C3E7541859702D00FA2921 /* btclogo.png in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E7501859702D00FA2921 /* btclogo.png */; }; 83C3E7551859702D00FA2921 /* btclogo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E7511859702D00FA2921 /* btclogo@2x.png */; }; - 83C3E7561859702D00FA2921 /* btclogoAlternate.png in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E7521859702D00FA2921 /* btclogoAlternate.png */; }; - 83C3E7571859702D00FA2921 /* btclogoAlternate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E7531859702D00FA2921 /* btclogoAlternate@2x.png */; }; 83C3E759185970C800FA2921 /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 83C3E758185970C800FA2921 /* Icon.icns */; }; + 90347B7918ACB073005C42A8 /* HuobiCNYFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 90347B7818ACB073005C42A8 /* HuobiCNYFetcher.m */; }; + B65127471A71A1BB00FB6A24 /* OKCoinCNYFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B65127461A71A1BB00FB6A24 /* OKCoinCNYFetcher.m */; }; + B662833B1A712EBB00520512 /* BitFinexUSDFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B662833A1A712EBB00520512 /* BitFinexUSDFetcher.m */; }; + B662833E1A715EFB00520512 /* WinkDexUSDFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B662833D1A715EFB00520512 /* WinkDexUSDFetcher.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -38,8 +38,6 @@ 830CE0A3171B6C3E00DDD525 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 830CE0AE171B6C3F00DDD525 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 838F4296182E2F6E00B4FC58 /* Fetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fetcher.h; sourceTree = ""; }; - 838F834E18602990007A7AAE /* btclogoDim.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = btclogoDim.png; path = Icons/btclogoDim.png; sourceTree = ""; }; - 838F834F18602990007A7AAE /* btclogoDim@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "btclogoDim@2x.png"; path = "Icons/btclogoDim@2x.png"; sourceTree = ""; }; 83C3E7411859700100FA2921 /* BitStampUSDFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BitStampUSDFetcher.h; path = Fetchers/BitStampUSDFetcher.h; sourceTree = ""; }; 83C3E7421859700100FA2921 /* BitStampUSDFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BitStampUSDFetcher.m; path = Fetchers/BitStampUSDFetcher.m; sourceTree = ""; }; 83C3E7431859700100FA2921 /* BTCeUSDFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BTCeUSDFetcher.h; path = Fetchers/BTCeUSDFetcher.h; sourceTree = ""; }; @@ -49,9 +47,15 @@ 83C3E74E1859702000FA2921 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MainMenu.xib; path = Views/MainMenu.xib; sourceTree = ""; }; 83C3E7501859702D00FA2921 /* btclogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = btclogo.png; path = Icons/btclogo.png; sourceTree = ""; }; 83C3E7511859702D00FA2921 /* btclogo@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "btclogo@2x.png"; path = "Icons/btclogo@2x.png"; sourceTree = ""; }; - 83C3E7521859702D00FA2921 /* btclogoAlternate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = btclogoAlternate.png; path = Icons/btclogoAlternate.png; sourceTree = ""; }; - 83C3E7531859702D00FA2921 /* btclogoAlternate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "btclogoAlternate@2x.png"; path = "Icons/btclogoAlternate@2x.png"; sourceTree = ""; }; 83C3E758185970C800FA2921 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Icon.icns; path = ../Resources/Icon.icns; sourceTree = ""; }; + 90347B7718ACB073005C42A8 /* HuobiCNYFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HuobiCNYFetcher.h; path = Fetchers/HuobiCNYFetcher.h; sourceTree = ""; }; + 90347B7818ACB073005C42A8 /* HuobiCNYFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HuobiCNYFetcher.m; path = Fetchers/HuobiCNYFetcher.m; sourceTree = ""; }; + B65127451A71A1BB00FB6A24 /* OKCoinCNYFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OKCoinCNYFetcher.h; path = Fetchers/OKCoinCNYFetcher.h; sourceTree = ""; }; + B65127461A71A1BB00FB6A24 /* OKCoinCNYFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OKCoinCNYFetcher.m; path = Fetchers/OKCoinCNYFetcher.m; sourceTree = ""; }; + B66283391A712EBB00520512 /* BitFinexUSDFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BitFinexUSDFetcher.h; path = Fetchers/BitFinexUSDFetcher.h; sourceTree = ""; }; + B662833A1A712EBB00520512 /* BitFinexUSDFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BitFinexUSDFetcher.m; path = Fetchers/BitFinexUSDFetcher.m; sourceTree = ""; }; + B662833C1A715EFB00520512 /* WinkDexUSDFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WinkDexUSDFetcher.h; path = Fetchers/WinkDexUSDFetcher.h; sourceTree = ""; }; + B662833D1A715EFB00520512 /* WinkDexUSDFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WinkDexUSDFetcher.m; path = Fetchers/WinkDexUSDFetcher.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -134,10 +138,6 @@ 83C3E758185970C800FA2921 /* Icon.icns */, 83C3E7501859702D00FA2921 /* btclogo.png */, 83C3E7511859702D00FA2921 /* btclogo@2x.png */, - 83C3E7521859702D00FA2921 /* btclogoAlternate.png */, - 83C3E7531859702D00FA2921 /* btclogoAlternate@2x.png */, - 838F834E18602990007A7AAE /* btclogoDim.png */, - 838F834F18602990007A7AAE /* btclogoDim@2x.png */, ); name = Icons; sourceTree = ""; @@ -147,10 +147,18 @@ children = ( 83C3E7411859700100FA2921 /* BitStampUSDFetcher.h */, 83C3E7421859700100FA2921 /* BitStampUSDFetcher.m */, + B65127451A71A1BB00FB6A24 /* OKCoinCNYFetcher.h */, + B65127461A71A1BB00FB6A24 /* OKCoinCNYFetcher.m */, + B662833C1A715EFB00520512 /* WinkDexUSDFetcher.h */, + B662833D1A715EFB00520512 /* WinkDexUSDFetcher.m */, + B66283391A712EBB00520512 /* BitFinexUSDFetcher.h */, + B662833A1A712EBB00520512 /* BitFinexUSDFetcher.m */, 83C3E7431859700100FA2921 /* BTCeUSDFetcher.h */, 83C3E7441859700100FA2921 /* BTCeUSDFetcher.m */, 83C3E7451859700100FA2921 /* CoinbaseUSDFetcher.h */, 83C3E7461859700100FA2921 /* CoinbaseUSDFetcher.m */, + 90347B7718ACB073005C42A8 /* HuobiCNYFetcher.h */, + 90347B7818ACB073005C42A8 /* HuobiCNYFetcher.m */, ); name = Fetchers; sourceTree = ""; @@ -215,12 +223,8 @@ buildActionMask = 2147483647; files = ( 83C3E7541859702D00FA2921 /* btclogo.png in Resources */, - 83C3E7561859702D00FA2921 /* btclogoAlternate.png in Resources */, - 838F835118602990007A7AAE /* btclogoDim@2x.png in Resources */, 830CE09B171B6C3E00DDD525 /* InfoPlist.strings in Resources */, - 83C3E7571859702D00FA2921 /* btclogoAlternate@2x.png in Resources */, 83C3E759185970C800FA2921 /* Icon.icns in Resources */, - 838F835018602990007A7AAE /* btclogoDim.png in Resources */, 83C3E7551859702D00FA2921 /* btclogo@2x.png in Resources */, 83C3E74F1859702000FA2921 /* MainMenu.xib in Resources */, ); @@ -234,7 +238,11 @@ buildActionMask = 2147483647; files = ( 83C3E7491859700100FA2921 /* BitStampUSDFetcher.m in Sources */, + B662833B1A712EBB00520512 /* BitFinexUSDFetcher.m in Sources */, 830CE09D171B6C3E00DDD525 /* main.m in Sources */, + B65127471A71A1BB00FB6A24 /* OKCoinCNYFetcher.m in Sources */, + B662833E1A715EFB00520512 /* WinkDexUSDFetcher.m in Sources */, + 90347B7918ACB073005C42A8 /* HuobiCNYFetcher.m in Sources */, 83C3E74B1859700100FA2921 /* CoinbaseUSDFetcher.m in Sources */, 83C3E74A1859700100FA2921 /* BTCeUSDFetcher.m in Sources */, 830CE0A4171B6C3E00DDD525 /* AppDelegate.m in Sources */, diff --git a/btcbar/AppDelegate.h b/btcbar/AppDelegate.h index 880053b..593e414 100644 --- a/btcbar/AppDelegate.h +++ b/btcbar/AppDelegate.h @@ -8,16 +8,20 @@ #import "BitStampUSDFetcher.h" #import "CoinbaseUSDFetcher.h" #import "BTCeUSDFetcher.h" +#import "BitFinexUSDFetcher.h" +#import "WinkDexUSDFetcher.h" +#import "OKCoinCNYFetcher.h" +#import "HuobiCNYFetcher.h" @interface AppDelegate : NSObject { NSMenu *btcbarMainMenu; NSInteger currentFetcherTag; - + NSStatusItem *btcbarStatusItem; - + NSTimer *updateViewTimer; NSTimer *updateDataTimer; - + NSMutableArray *tickers; NSUserDefaults *prefs; } diff --git a/btcbar/AppDelegate.m b/btcbar/AppDelegate.m index 869ec17..cf89924 100644 --- a/btcbar/AppDelegate.m +++ b/btcbar/AppDelegate.m @@ -17,34 +17,37 @@ - (void)awakeFromNib { // Load ticker preference from disk prefs = [NSUserDefaults standardUserDefaults]; - + // Register update notifications for tickers [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTickerNotification:) name:@"btcbar_ticker_update" object:nil]; - + // Pass each ticker object into a dictionary, get first updates tickers = [NSMutableArray arrayWithObjects: [[BitStampUSDFetcher alloc] init], [[BTCeUSDFetcher alloc] init], [[CoinbaseUSDFetcher alloc] init], + [[BitFinexUSDFetcher alloc] init], + [[WinkDexUSDFetcher alloc] init], + [[OKCoinCYNFetcher alloc] init], + [[HuobiCNYFetcher alloc] init], nil]; - - + // If ticker preference does not exist, default to 0 if (![prefs integerForKey:@"btcbar_ticker"]) [prefs setInteger:0 forKey:@"btcbar_ticker"]; currentFetcherTag = [prefs integerForKey:@"btcbar_ticker"]; - + // If ticker preference exceeds the bounds of `tickers`, default to 0 if (currentFetcherTag < 0 || currentFetcherTag >= [tickers count]) currentFetcherTag = 0; - + // Initialize main menu btcbarMainMenu = [[NSMenu alloc] initWithTitle:@"loading..."]; - + // Add each loaded ticker object to main menu for(id ticker in tickers) { @@ -52,26 +55,26 @@ - (void)awakeFromNib new_menuitem.tag = [tickers indexOfObject:ticker]; [btcbarMainMenu addItem:new_menuitem]; } - + // Add the separator, Open in Browser, and Quit items to main menu [btcbarMainMenu addItem:[NSMenuItem separatorItem]]; [btcbarMainMenu addItem:[[NSMenuItem alloc] initWithTitle:@"Open in Browser" action:@selector(menuActionBrowser:) keyEquivalent:@""]]; [btcbarMainMenu addItem:[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(menuActionQuit:) keyEquivalent:@"q"]]; - + // Set the default ticker's menu item state to checked [[btcbarMainMenu.itemArray objectAtIndex:currentFetcherTag] setState:NSOnState]; - + // Initialize status bar item with flexible width btcbarStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; - // Set status bar image and highlighted image - btcbarStatusItem.image = [NSImage imageNamed:@"btclogoDim"]; - btcbarStatusItem.alternateImage = [NSImage imageNamed:@"btclogoAlternate"]; + // Set status bar image + NSImage *image = [NSImage imageNamed:@"btclogo"]; + [image setTemplate:YES]; + [btcbarStatusItem setImage:image]; // Set menu options on click - btcbarStatusItem.highlightMode = YES; btcbarStatusItem.menu = btcbarMainMenu; - + // Setup timer to update all tickers every 10 seconds updateDataTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(updateDataTimerAction:) userInfo:nil repeats:YES]; } @@ -87,18 +90,18 @@ - (void)menuActionSetTicker:(id)sender // Set all menu items to "off" state for (NSMenuItem *menuitem in btcbarMainMenu.itemArray) menuitem.state = NSOffState; - + // Set this menu item to "on" state [sender setState:NSOnState]; - + // Update ticker preference currentFetcherTag = [sender tag]; [prefs setInteger:currentFetcherTag forKey:@"btcbar_ticker"]; [prefs synchronize]; - + // Update the requested ticker immediately [[tickers objectAtIndex:currentFetcherTag] requestUpdate]; - + // Force the status item value to update [[NSNotificationCenter defaultCenter] postNotificationName:@"btcbar_ticker_update" object:[tickers objectAtIndex:currentFetcherTag]]; @@ -134,7 +137,7 @@ -(void)handleTickerNotification:(NSNotification *)pNotification // Set the ticker value in the menu to the short error [[[btcbarMainMenu itemArray] objectAtIndex:[tickers indexOfObject:[pNotification object]]] setTitle:[NSString stringWithFormat:@"[%@] %@",[[pNotification object] error].localizedDescription, [[pNotification object] ticker_menu]]]; } - + // If this notification is for the currently selected ticker, update the status item too if ([pNotification object] == [tickers objectAtIndex:currentFetcherTag]) { @@ -152,7 +155,7 @@ -(void)handleTickerNotification:(NSNotification *)pNotification btcbarStatusItem.toolTip = [[tickers objectAtIndex:currentFetcherTag] ticker_menu]; } } - + } // Requests for each Fetcher to update itself diff --git a/btcbar/Fetchers/BitFinexUSDFetcher.h b/btcbar/Fetchers/BitFinexUSDFetcher.h new file mode 100644 index 0000000..192b7c6 --- /dev/null +++ b/btcbar/Fetchers/BitFinexUSDFetcher.h @@ -0,0 +1,22 @@ +// +// BitFinexUSDFetcher.h +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import +#import "Fetcher.h" + +@interface BitFinexUSDFetcher : NSObject + +@property (nonatomic) NSString* ticker; +@property (nonatomic) NSString* ticker_menu; +@property (nonatomic) NSString* url; +@property (nonatomic) NSError* error; +@property (nonatomic) NSMutableData *responseData; + +- (void)requestUpdate; + +@end diff --git a/btcbar/Fetchers/BitFinexUSDFetcher.m b/btcbar/Fetchers/BitFinexUSDFetcher.m new file mode 100644 index 0000000..a135db4 --- /dev/null +++ b/btcbar/Fetchers/BitFinexUSDFetcher.m @@ -0,0 +1,121 @@ +// +// BitFinexUSDFetcher.m +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import "BitFinexUSDFetcher.h" + +@implementation BitFinexUSDFetcher + +- (id)init +{ + if (self = [super init]) + { + // Menu Item Name + self.ticker_menu = @"BitFinexUSD"; + + // Website location + self.url = @"https://www.bitfinex.com/"; + + // Immediately request first update + [self requestUpdate]; + } + + return self; +} + +// Override Ticker setter to trigger status item update +- (void)setTicker:(NSString *)tickerString +{ + // Update the ticker value + _ticker = tickerString; + + // Trigger notification to update ticker + [[NSNotificationCenter defaultCenter] postNotificationName:@"btcbar_ticker_update" object:self]; +} + +// Initiates an asyncronous HTTP connection +- (void)requestUpdate +{ + // More information on the API + // can be found here: https://www.bitfinex.com/pages/api + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.bitfinex.com/v1/pubticker/BTCUSD/"]]; + + // Set the request's user agent + [request addValue:@"btcbar/2.0 (BitFinexUSDFetcher)" forHTTPHeaderField:@"User-Agent"]; + + // Initialize a connection from our request + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + // Go go go + [connection start]; +} + +// Initializes data storage on request response +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + self.responseData = [[NSMutableData alloc] init]; +} + +// Appends response data +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.responseData appendData:data]; +} + +// Indiciate no caching +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + return nil; +} + +// Parse data after load +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // Parse the JSON into results + NSError *jsonParsingError = nil; + NSDictionary *results = [[NSDictionary alloc] init]; + results = [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:&jsonParsingError]; + + // Results parsed successfully from JSON + if(results) + { + // Get API status + NSString *resultsStatus = [results objectForKey:@"last_price"]; + + // If API call succeeded update the ticker... + if(resultsStatus) + { + NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init]; + currencyStyle.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + currencyStyle.numberStyle = NSNumberFormatterCurrencyStyle; + resultsStatus = [currencyStyle stringFromNumber:[NSDecimalNumber decimalNumberWithString:resultsStatus]]; + self.ticker = resultsStatus; + } + // Otherwise log an error... + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"API Error", NSLocalizedDescriptionKey, @"The JSON received did not contain a result or the API returned an error.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } + } + // JSON parsing failed + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"JSON Error", NSLocalizedDescriptionKey, @"Could not parse the JSON returned.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } +} + +// HTTP request failed +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"Connection Error", NSLocalizedDescriptionKey, @"Could not connect to BitFinex.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; +} + + +@end diff --git a/btcbar/Fetchers/HuobiCNYFetcher.h b/btcbar/Fetchers/HuobiCNYFetcher.h new file mode 100644 index 0000000..151a073 --- /dev/null +++ b/btcbar/Fetchers/HuobiCNYFetcher.h @@ -0,0 +1,20 @@ +// +// HuobiCNYFetcher.h +// btcbar +// +// Created by lwei on 2/13/14. +// Copyright (c) 2014 nearengine. All rights reserved. +// + +#import +#import "Fetcher.h" + +@interface HuobiCNYFetcher : NSObject + +@property (nonatomic) NSString* ticker; +@property (nonatomic) NSString* ticker_menu; +@property (nonatomic) NSString* url; +@property (nonatomic) NSError* error; +@property (nonatomic) NSMutableData *responseData; + +@end diff --git a/btcbar/Fetchers/HuobiCNYFetcher.m b/btcbar/Fetchers/HuobiCNYFetcher.m new file mode 100644 index 0000000..58d1a53 --- /dev/null +++ b/btcbar/Fetchers/HuobiCNYFetcher.m @@ -0,0 +1,130 @@ +// +// HuobiCNYFetcher.m +// btcbar +// +// Created by lwei on 2/13/14. +// Copyright (c) 2014 nearengine. All rights reserved. +// + +#import "HuobiCNYFetcher.h" + +@implementation HuobiCNYFetcher + +- (id)init +{ + if (self = [super init]) + { + // Menu Item Name + self.ticker_menu = @"HuobiCNY"; + + // Website location + self.url = @"http://www.huobi.com"; + + // Immediately request first update + [self requestUpdate]; + } + + return self; +} + +// Override Ticker setter to trigger status item update +- (void)setTicker:(NSString *)tickerString +{ + // Update the ticker value + _ticker = tickerString; + + // Trigger notification to update ticker + [[NSNotificationCenter defaultCenter] postNotificationName:@"btcbar_ticker_update" object:self]; +} + +// Initiates an asyncronous HTTP connection +- (void)requestUpdate +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://market.huobi.com/staticmarket/detail.html"]]; + + // Set the request's user agent + [request addValue:@"btcbar/2.0 (HuobiCNYFetcher)" forHTTPHeaderField:@"User-Agent"]; + + // Initialize a connection from our request + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + // Go go go + [connection start]; +} + +// Initializes data storage on request response +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + self.responseData = [[NSMutableData alloc] init]; +} + +// Appends response data +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.responseData appendData:data]; +} + +// Indiciate no caching +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + return nil; +} + +// Parse data after load +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSString *responseStr = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + if (!responseStr) { + return; + } + + NSRange range = [responseStr rangeOfString:@"(?<=\\().*(?=\\))" options:NSRegularExpressionSearch]; + if (range.location > responseStr.length || range.location + range.length > responseStr.length) { + return; + } + + NSString *resultsStr = [responseStr substringWithRange:range]; + + if (!resultsStr) { + return; + } + + // Parse the JSON into results + NSError *jsonParsingError = nil; + id results = [NSJSONSerialization JSONObjectWithData:[resultsStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&jsonParsingError]; + + // Results parsed successfully from JSON + if (results) + { + NSNumber *ticker = [results objectForKey:@"p_new"]; + if (ticker) { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + NSString *resultsStatus = [numberFormatter stringFromNumber:ticker]; + resultsStatus = [NSString stringWithFormat:@"¥%@", resultsStatus]; + + self.error = nil; + self.ticker = resultsStatus; + } + // Otherwise log an error... + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"API Error", NSLocalizedDescriptionKey, @"The JSON received did not contain a result or the API returned an error.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } + } + // JSON parsing failed + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"JSON Error", NSLocalizedDescriptionKey, @"Could not parse the JSON returned.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } +} + +// HTTP request failed +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"Connection Error", NSLocalizedDescriptionKey, @"Could not connect to Huobi.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; +} + +@end diff --git a/btcbar/Fetchers/OKCoinCNYFetcher.h b/btcbar/Fetchers/OKCoinCNYFetcher.h new file mode 100644 index 0000000..dabc41e --- /dev/null +++ b/btcbar/Fetchers/OKCoinCNYFetcher.h @@ -0,0 +1,22 @@ +// +// OKCoinCYNFetcher.h +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import +#import "Fetcher.h" + +@interface OKCoinCYNFetcher : NSObject + +@property (nonatomic) NSString* ticker; +@property (nonatomic) NSString* ticker_menu; +@property (nonatomic) NSString* url; +@property (nonatomic) NSError* error; +@property (nonatomic) NSMutableData *responseData; + +- (void)requestUpdate; + +@end diff --git a/btcbar/Fetchers/OKCoinCNYFetcher.m b/btcbar/Fetchers/OKCoinCNYFetcher.m new file mode 100644 index 0000000..ae666f4 --- /dev/null +++ b/btcbar/Fetchers/OKCoinCNYFetcher.m @@ -0,0 +1,121 @@ +// +// OKCoinCYNFetcher.m +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import "OKCoinCNYFetcher.h" + +@implementation OKCoinCYNFetcher + +- (id)init +{ + if (self = [super init]) + { + // Menu Item Name + self.ticker_menu = @"OKCoinCNY"; + + // Website location + self.url = @"https://www.okcoin.cn/"; + + // Immediately request first update + [self requestUpdate]; + } + + return self; +} + +// Override Ticker setter to trigger status item update +- (void)setTicker:(NSString *)tickerString +{ + // Update the ticker value + _ticker = tickerString; + + // Trigger notification to update ticker + [[NSNotificationCenter defaultCenter] postNotificationName:@"btcbar_ticker_update" object:self]; +} + +// Initiates an asyncronous HTTP connection +- (void)requestUpdate +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.okcoin.cn/api/v1/ticker.do?symbol=btc_cny"]]; + + // Set the request's user agent + [request addValue:@"btcbar/2.0 (OKCoinCNYFetcher)" forHTTPHeaderField:@"User-Agent"]; + + // Initialize a connection from our request + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + // Go go go + [connection start]; +} + +// Initializes data storage on request response +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + self.responseData = [[NSMutableData alloc] init]; +} + +// Appends response data +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.responseData appendData:data]; +} + +// Indiciate no caching +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + return nil; +} + +// Parse data after load +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // Parse the JSON into results + NSError *jsonParsingError = nil; + NSDictionary *results = [[NSDictionary alloc] init]; + results = [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:&jsonParsingError]; + + // Results parsed successfully from JSON + if(results) + { + // Get API status + NSDictionary *ticker = [results objectForKey:@"ticker"]; + NSString *resultsStatus = [ticker objectForKey:@"last"]; + + + // If API call succeeded update the ticker... + if(resultsStatus) + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + resultsStatus = [numberFormatter stringFromNumber:[NSDecimalNumber decimalNumberWithString:resultsStatus]]; + resultsStatus = [NSString stringWithFormat:@"¥%@", resultsStatus]; + + self.ticker = resultsStatus; + } + // Otherwise log an error... + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"API Error", NSLocalizedDescriptionKey, @"The JSON received did not contain a result or the API returned an error.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } + } + // JSON parsing failed + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"JSON Error", NSLocalizedDescriptionKey, @"Could not parse the JSON returned.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } +} + +// HTTP request failed +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"Connection Error", NSLocalizedDescriptionKey, @"Could not connect to OKCoin.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; +} + + +@end diff --git a/btcbar/Fetchers/WinkDexUSDFetcher.h b/btcbar/Fetchers/WinkDexUSDFetcher.h new file mode 100644 index 0000000..182ed1f --- /dev/null +++ b/btcbar/Fetchers/WinkDexUSDFetcher.h @@ -0,0 +1,22 @@ +// +// WinkDexUSDFetcher.h +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import +#import "Fetcher.h" + +@interface WinkDexUSDFetcher : NSObject + +@property (nonatomic) NSString* ticker; +@property (nonatomic) NSString* ticker_menu; +@property (nonatomic) NSString* url; +@property (nonatomic) NSError* error; +@property (nonatomic) NSMutableData *responseData; + +- (void)requestUpdate; + +@end diff --git a/btcbar/Fetchers/WinkDexUSDFetcher.m b/btcbar/Fetchers/WinkDexUSDFetcher.m new file mode 100644 index 0000000..d45198a --- /dev/null +++ b/btcbar/Fetchers/WinkDexUSDFetcher.m @@ -0,0 +1,125 @@ +// +// WinkDexUSDFetcher.m +// btcbar +// +// Created by Tim Daubenschütz on 22/01/15. +// Copyright (c) 2015 nearengine. All rights reserved. +// + +#import "WinkDexUSDFetcher.h" + +@implementation WinkDexUSDFetcher + +- (id)init +{ + if (self = [super init]) + { + // Menu Item Name + self.ticker_menu = @"WinkDexUSD"; + + // Website location + self.url = @"https://winkdex.com/"; + + // Immediately request first update + [self requestUpdate]; + } + + return self; +} + +// Override Ticker setter to trigger status item update +- (void)setTicker:(NSString *)tickerString +{ + // Update the ticker value + _ticker = tickerString; + + // Trigger notification to update ticker + [[NSNotificationCenter defaultCenter] postNotificationName:@"btcbar_ticker_update" object:self]; +} + +// Initiates an asyncronous HTTP connection +- (void)requestUpdate +{ + // Documentation for this API + // can be found here: http://docs.winkdex.com/#get-request + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://winkdex.com/api/v0/price"]]; + + // Set the request's user agent + [request addValue:@"btcbar/2.0 (WinkDexUSDFetcher)" forHTTPHeaderField:@"User-Agent"]; + + // Initialize a connection from our request + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + // Go go go + [connection start]; +} + +// Initializes data storage on request response +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + self.responseData = [[NSMutableData alloc] init]; +} + +// Appends response data +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.responseData appendData:data]; +} + +// Indiciate no caching +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + return nil; +} + +// Parse data after load +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + // Parse the JSON into results + NSError *jsonParsingError = nil; + NSDictionary *results = [[NSDictionary alloc] init]; + results = [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:&jsonParsingError]; + + // Results parsed successfully from JSON + if(results) + { + // Get API status + NSString *resultsStatus = [results objectForKey:@"price"]; + + // If API call succeeded update the ticker... + if(resultsStatus) + { + NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init]; + currencyStyle.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + currencyStyle.numberStyle = NSNumberFormatterCurrencyStyle; + + // We first need to convert the price form pennys to dollars + // then we can asign it to the view. + NSDecimalNumber *price = [[NSDecimalNumber alloc] initWithDouble:[resultsStatus doubleValue]/100]; + resultsStatus = [currencyStyle stringFromNumber:price]; + self.ticker = resultsStatus; + } + // Otherwise log an error... + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"API Error", NSLocalizedDescriptionKey, @"The JSON received did not contain a result or the API returned an error.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } + } + // JSON parsing failed + else + { + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"JSON Error", NSLocalizedDescriptionKey, @"Could not parse the JSON returned.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; + } +} + +// HTTP request failed +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + self.error = [NSError errorWithDomain:@"com.nearengine.btcbar" code:0 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"Connection Error", NSLocalizedDescriptionKey, @"Could not connect to WinkDex.", NSLocalizedFailureReasonErrorKey, nil]]; + self.ticker = nil; +} + + +@end diff --git a/btcbar/Icons/btclogo.png b/btcbar/Icons/btclogo.png index 898e647..a6d0200 100644 Binary files a/btcbar/Icons/btclogo.png and b/btcbar/Icons/btclogo.png differ diff --git a/btcbar/Icons/btclogo@2x.png b/btcbar/Icons/btclogo@2x.png index 7d711ee..b2f82e5 100644 Binary files a/btcbar/Icons/btclogo@2x.png and b/btcbar/Icons/btclogo@2x.png differ diff --git a/btcbar/Icons/btclogoAlternate.png b/btcbar/Icons/btclogoAlternate.png deleted file mode 100644 index f2cf803..0000000 Binary files a/btcbar/Icons/btclogoAlternate.png and /dev/null differ diff --git a/btcbar/Icons/btclogoAlternate@2x.png b/btcbar/Icons/btclogoAlternate@2x.png deleted file mode 100644 index 893b51e..0000000 Binary files a/btcbar/Icons/btclogoAlternate@2x.png and /dev/null differ diff --git a/btcbar/Icons/btclogoDim.png b/btcbar/Icons/btclogoDim.png deleted file mode 100644 index ecc2e02..0000000 Binary files a/btcbar/Icons/btclogoDim.png and /dev/null differ diff --git a/btcbar/Icons/btclogoDim@2x.png b/btcbar/Icons/btclogoDim@2x.png deleted file mode 100644 index 12fe287..0000000 Binary files a/btcbar/Icons/btclogoDim@2x.png and /dev/null differ diff --git a/btcbar/btcbar-Info.plist b/btcbar/btcbar-Info.plist index 46697a7..ccd3218 100644 --- a/btcbar/btcbar-Info.plist +++ b/btcbar/btcbar-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.1.4 + 2.2.0 CFBundleSignature ???? CFBundleVersion