১০- অবজেকটিভ সি তে এক্সেপশন ও এরর হ্যান্ডেলিং

Standard

ভুমিকাঃ
অ্যাপ্লিকেশন ডেভেলপমেন্ট এর সময় যে দুটি প্রধান সমস্যার কথা ডেভেলপার/প্রোগ্রামারদের ভাবতে হয় সেগুলো হল এক্সেপশন ও এরর। iOS ও OS X এ অ্যাপ্লিকেশন চলার সময়ও এক্সেপশন ও এরর ঘটতে পারে।

প্রোগ্রামার লেভেলের বাগ গুলো কে এক্সেপশন বলা হয়। উদাহরনস্বরুপ: যখন কোডের মাধ্যমে কোন অ্যারে (Array) এর ননএক্সিস্টিং ইলিমেন্টকে এক্সেস করার চেষ্টা করা হয় তখন সিস্টেম এক্সেপশন থ্রো করে। অ্যাপ্লিকেশন চলার সময় যদি কোন কারনে এরকম একটা বিষয় উদ্ভূত হয় তাহলে ডেভেলপারকে সেটা সম্পর্কে তথ্য দেয়ার জন্যই প্রোগ্রামিং ল্যাঙ্গুয়েজগুলোতে এক্সেপশন ফিচার ডিজাইন করা হয়েছে। প্রোগ্রামের মধ্যেই প্রোগ্রামারকে এরকম এক্সেপশনাল কেস হ্যান্ডেল করার জন্য প্রয়োজনীয় কোড লিখতে হয় যাকে বলে এক্সেপশন হ্যান্ডেলিং।
Screen Shot 2014-06-10 at 3

এক্সেপশন ডেভেলপার কে জানাও।

অন্যদিকে ইউজার লেভেলে যদি কোন অনাকাঙ্খিত ঘটনা ঘটে সেগুলোকে এরর বলা হয়। যেমন, যদি আপনার অ্যাপ্লিকেশনটির কাজ হয় যে, ডিভাইসের GPS সার্ভিস ব্যবহার করে আসেপাশের কিছু লোকেশন দেখাবে। কিন্তু ইউজার তার ডিভাইসের GPS সেটিং ডিজ্যাবল করে রেখেছে । তখন অ্যাপটা চলার সময় ইউজারকে একটা ম্যাসেজ দেখানো উচিত যে, তার GPS অফ আছে। এরকম অবস্থায় ইউজারকে কিছুই না জানিয়ে অ্যাপ্লিকেশন এর ক্র্যাশ হওয়া উচিত হবে না। তাই এধরনের কন্ডিশনগুলো যখন ঘটবে তখন ম্যানুয়ালী চেক করে সঠিক তথ্যটা বা এরর ম্যাসেজটা ইউজারকে দেওয়াই হচ্ছে এরর হ্যান্ডেলিং।
Screen Shot 2014-06-10 at 3.09.58 PM

ওয়ার্ক এরর ইউজারকে জানাও এবং সম্ভব হলে অ্যাপ রান করতেই থাকো।

কিন্তু মনে রাখবেন, কোডিং এরর আর উপরে উল্লেখিত এররটা কিন্তু এক জিনিস না। যেমন, আপনি আপনার প্রোগ্রামের মধ্যে একটা ক্লাস ইন্সট্যান্সিয়েট করে সেটার কিছু মেথড এক্সেস করতে চাচ্ছেন কিন্তু শুরুতে ওই ক্লাসের হেডার ফাইলকে ইনক্লুড করেন নাই। তখন কিন্তু কম্পাইলার আপনাকে এরর দেবে এবং প্রোগ্রাম রান-ই করবে না। কম্পাইলারকে ডিজাইন করা হয়েছে এরকম কোডিং এরর সম্পর্কে ডেভেলপারকে জানাতে।
Screen Shot 2014-06-10 at 3.17.02 PM

কোডিং এরর ডেভেলপারকে জানিয়ে থেমে যাও

এক্সেপশন (Exceptions)ঃ
অবজেকটিভ-সি তে NSException ক্লাস দিয়ে এক্সেপশনকে রিপ্রেজেন্ট করা হয়। যখন কোন এক্সেপশন ঘটে তখন এই এক্সেপশনের যাবতীয় তথ্য NSException ক্লাসের ইনস্ট্যান্স থেকেই পাওয়া যায়। NSException ক্লাসের ৩টি প্রধান প্রোপার্টি নিচে দেওয়া হলঃ

  • name ঃ এক্সেপশনের নাম (ডাটাটাইপ: NSString ) যা সকল এক্সেপশনের জন্য আলাদা
  • reason ঃ এক্সেপশনের সহজবোধ্য বর্ননা ( ডাটাটাইপ : NSString)
  • userInfo ঃ NSDictionary টাইপের অবজেক্ট। এই অবজেক্টে কি-ভ্যালু রিলেশনশিপ দিয়ে এক্সেপশনের অতিরিক্ত তথ্য থাকে

এক্সেপশন হ্যান্ডেলিং (Exception Handling) ঃ
নিচের মত করে একটি নতুন ফ্রেশ প্রজেক্টের main.m ফাইলটি এডিট করে ফেলিঃ

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        // menuList of the restaurant
        NSArray *menuList = @[@"Vegetable Rice", @"Chicken Toast", @"Thai Soup"];
        
        NSLog(@"1st Item Name is %@",  menuList[0]); // Vegetable Rice
        NSLog(@"2nd Item Name is %@",  menuList[1]);  // Chicken Toast
        NSLog(@"3rd Item Name is %@",  menuList[2]);; // Thai Soup
        NSLog(@"3rd Item Name is %@",  menuList[3]);  // throughs exception
    }
    return 0;
}

উপরের প্রোগ্রামটিতে প্রথমেই NSArray টাইপ একটি অ্যারে লিস্ট নেওয়া হয়েছে যাতে ৩ টি আইটেম আছে। আমরা জানি যে অবজেকটিভ সি এবং প্রায় সকল প্রোগ্রামিং লাঙ্গুয়েজেই ০ থেকে ইনডেক্সিং শুরু হয়। সুতরাং menuList অ্যারেতে ০ থেকে ২ এই তিনটি ইনডেক্স রয়েছে। কিন্তু প্রোগ্রামটিতে ০ থেকে ৩ পর্যন্ত ইনডেক্স এক্সেস করা হয়েছে। প্রোগ্রামটিতে যদিও কোন সিন্ট্যাক্স এরর নেই তবুও এই প্রোগ্রামটি রান করানো হলে কনসোলে নিচের মত একটি মেসেজ ডিসপ্লে হবে।

2014-06-05 23:14:01.320 restaurant[598:303] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff9366641c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8e791e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff935521df -[__NSArrayI objectAtIndex:] + 175
	3   restaurant                          0x0000000100001798 main + 376
	4   libdyld.dylib                       0x00007fff8a1405fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

প্রোগ্রামটি রান করলে এই এক্সেপশনটি থ্রো হয় এবং তারপর প্রোগ্রামটি ক্র্যাশ করে। কিভাবে হল? menuList এ তিনটি খাবারের নাম আছে যাদের ইনডেক্স যথাক্রমে ০, ১, ২। প্রোগ্রামটি কম্পাইল করলে কোন রকম এরর বা অন্য কোন ধরনের অস্বাভাবিক ঘটনা ঘটে না। কিন্তু কম্পাইলেশনের পর রান করালে শেষলাইনটি এক্সিকিউশনের সময় menuList[3] দিয়ে menuList এর ৩ নং ইনডেক্সের ভ্যালু এক্সেস করা হয়, তখনই এক্সেপশন (ব্যতিক্রম) ঘটে এবং প্রোগ্রামটি ক্র্যাশ করে কারন menuList অ্যারেতে ০-২ এই তিনটি মাত্র ইনডেক্সই আছে।

এভাবে অনেক কারনেই অ্যাপ্লিকেশনটি রান করানোর পর এক্সেপশন হয়ে ক্র্যাশ হতে পারে যা মোটেই কাম্য নয়। তাই প্রোগ্রামেই যেখানে যেখানে এধরনের এক্সেপশন হওয়ার সম্ভাবনা থাকে সেসব জায়গায় এক্সেপশন হ্যান্ডেল করতে হয়।

অন্যান্য হাই লেভেল প্রোগ্রামিং ল্যাঙ্গুয়েজের মতই অবজেকটিভ-সি তেও try-catch-finally প্যাটার্নে এক্সেপশন হ্যান্ডেল করা হয়। প্রথমেই কোডের যেসব লাইনে এক্সেপশন হতে পারে সেগুলোকে @try ব্লকের ভেতরে লেখা হয়। যদি কোন এক্সেপশন ঘটে তাহলে @catch ব্লকে এক্সেপশনের ধরন জেনে এক্সেপশনটিকে হ্যান্ডেল করতে হয়। অবশেষে @finally ব্লকের কোড গুলো এক্সিকিউট করা হয়। এক্সেপশন হোক আর না হোক @finally ব্লক এক্সিকিউট হবেই।

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        // menuList of the restaurant
        NSArray *menuList = @[@"Vegetable Rice", @"Chicken Toast", @"Thai Soup"];
        
        @try{
          // write codes where exception can be thrown
          NSLog(@"4th Item Name is %@",  menuList[3]);
        }
        @catch(NSException *theException){
            // exception occurred. Lets handle this exception
            NSLog(@"An Exception named %@ has occurred@",  theException.name);
            NSLog(@"Some more details about the exception : %@",  theException.reason);
        }
        @finally{
            NSLog(@"Continues without crashing the application");
        }
        
        NSLog(@"This is being displayed. That means this application didn't crash.");
    }
    return 0;
}

উপরের main.m কে রান করলে @try ব্লকে menuList এর ৪র্থ আইটেম এক্সেস করার সাথে সাথেই এক্সেপশন থ্রো হয়। @catch ব্লকে NSException এর *theException এ এক্সেপশনটি অ্যাসাইন হয়। NSException ক্লাসের অবজেক্ট theException এর প্রোপার্টিগুলো name, reason, userInfo তে এক্সেপশনটি সম্পর্কে তথ্য পাওয়া যাচ্ছে।

রিয়েল লাইফ অ্যাপ্লিকেশনগুলো তে @catch ব্লকে সাধারনত এক্সেপশনের name ও reason থেকে তথ্য নিয়ে সমস্যার সমাধান করে অথবা ইউজারকে সহজবোধ্য এরর মেসেজ দিয়ে প্রোগ্রামটি কে নরমালি এক্সিট/কন্টিনিউ করা হয়।

অবজেকটিভ-সি এর এক্সেপশন হ্যান্ডেলিং খুব বেশী ইফিশিয়েন্ট না হওয়ায় @try/@catch ব্লক ব্যবহার না করে যদি সাধারন If স্টেইটমেন্ট দিয়ে চেক করে সমস্যা বাইপাস করা যায় তাহলে সেটা করাই ভাল। একান্তই যদি দরকার পড়ে সেক্ষেত্রে উপরের মত @catch ব্লকে ইউজার কে এক্সেপশনের সহজবোধ্য এরর মেসেজ দিয়ে প্রোগ্রামটি এক্সিট করে দেওয়া যেতে পারে।

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        // menuList of the restaurant
        NSArray *menuList = @[@"Vegetable Rice", @"Chicken Toast", @"Thai Soup"];
        
        int selectedIndex = 3;
        
        if(selectedIndex < [menuList count]){
            NSLog(@"4th Item Name is %@",  menuList[selectedIndex]);
        }
        else
        {
            //Exception is avoided
        }
    }
    return 0;
}

এই প্রোগ্রামটিতে শুরুতেই selectedIndex এর ভ্যালু চেক করে নেওয়া হচ্ছে। যদি selectedIndex টি menuList এর count এর চেয়ে ছোট হয় তাহলে ওই ইনডেক্সের ভ্যালু এক্সেস করা হচ্ছে। selectedIndex এর ভ্যালু যদি menuList এর count এর সমান বা বড় হয় তাহলে ওই ইনডেক্সের ভ্যালু এক্সেস না করে লাইনটিকে বাইপাস করা হচ্ছে।

বিল্ট-ইন এক্সেপশন (Built-in Exceptions) ঃ
স্ট্যান্ডার্ড iOS ও OS X ফ্রেমওয়ার্ক এ অনেকগুলো বিল্ট-ইন এক্সেপশন রয়েছে যেগুলো দিয়ে মোটামুটি সবরকম এক্সেপশন @catch ব্লকে ধরা যায়। সবচেয়ে বেশী ব্যবহৃত এক্সেপশন গুলো সম্পর্কে নিচে ২-১ লাইন লেখা হলঃ

  • NSRangeException ঃ অ্যারে বা কালেকশনের বাইরের ইনডেক্সের ইলিমেন্ট এক্সেস করলে NSRangeException থ্রো হয়।
  • NSInvalidArgumentException ঃ মেথডে ইনভ্যালিড আরগুমেন্ট পাঠানো হলে NSInvalidArgumentException থ্রো হয়।
  • NSInternalInconsistencyException ঃ ইন্টারনালী যদি কোন অস্বাভাবিক কিছু ঘটে যা প্রোগ্রাম ক্র্যাশ করায় তখন NSInvalidArgumentException হয়।
  • NSGenericException ঃ যখন কোন স্পেসিফিক এক্সেপশন না ধরে জেনেরিক এক্সেপশন ধরতে হয় তখন NSGenericException ক্লাসের অবজেক্ট দিয়ে এক্সেপশনটি ধরা হয়। অর্থাত যেকোন কারনেই এক্সেপশন থ্রো হউক না কেন NSGenericException থ্রো হয়।

খেয়াল করুন যে এই সব এক্সেপশন গুলোর ভ্যালু স্ট্রিং। এদের NSException ক্লাসের সাবক্লাস ভাবার দরকার নেই। তাই কোন স্পেসিফিক এক্সেপশন পেতে চাইলে name প্রোপার্টি চেক করতে হবে।

// main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        // menuList of the restaurant
        NSArray *menuList = @[@"Vegetable Rice", @"Chicken Toast", @"Thai Soup"];
        
        @try{
            // write codes where exception can be thrown
            NSLog(@"4th Item Name is %@",  menuList[3]);
        }
        @catch(NSException *theException){
            // exception occured. Lets handle this exception
           if(theException.name == NSRangeException)
               NSLog(@"NSRangeException Exception is caught.");
            
        }
        @finally{
            NSLog(@"Continues without crashing the application");
        }
        
        NSLog(@"This is being displayed. That means this application didnt crashed.");
    }
    return 0;
}

কাস্টম এক্সেপশন (Custom Exceptions) ঃ
@throw ডিরেক্টিভ দিয়ে সহজেই কাস্টম এক্সেপশন থ্রো করানো যায়। যেখানে এক্সেপশন থ্রো হবে সেখানে NSException ক্লাসের ইন্স্ট্যান্স বানিয়ে exceptionWithName:reason:userInfo: ফ্যাক্টরী মেথড দিয়ে কাস্টম এক্সেপশন ইনস্ট্যান্স বানানো যায়। বোঝার জন্য নিচের উদাহরনটি দেখিঃ

// main.m
#import <Foundation/Foundation.h>

NSString *getRandomItemFromMenuList(NSArray *menuList) {
    int max = (int)[menuList count];
    if (max == 0) {
        NSException *e = [NSException
                          exceptionWithName:@"EmptyMenuListException"
                          reason:@"*** The list has no food!"
                          userInfo:nil];
        @throw e;
    }
    int randomIndex = arc4random_uniform(max);
    return menuList[randomIndex];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        @try {
            NSString *foodItem = getRandomItemFromMenuList(@[]);
            NSLog(@"The selected food is: %@", foodItem);
        } @catch(NSException *theException) {
            if (theException.name == @"EmptyMenuListException") {
                NSLog(@"Caught an EmptyMenuListException");
            } else {
                NSLog(@"Ignored a %@ exception", theException);
                @throw;
            }
        }
    }    return 0;
}

@throw ডিরেক্টিভ খুবই এক্সপেনসিভ অপারেশন। এই অপারেশনটি এক্সিকিউট করতে অপারেটিং সিস্টেম এর প্রচুর রিসোর্স খরচ হয়। তাই @throw ডিরেক্টিভ ব্যবহার না করে অবজেকটিভ সি এর ন্যাটিভ এক্সেপশন গুলো ব্যবহার করাই উত্তম।

এরর (Errors)ঃ
প্রোগ্রামে এমন অনেক অপারেশন থাকতে পারে যেগুলো ফেইল করলেও প্রোগ্রাম ক্র্যাশ করে না। এধরনের সমস্যাগুলো কে এরর বলা হয়। যেমন রানটাইমে ইউজারকে যদি কোন ফাইল ইনপুট দিতে বলা হয়, সেক্ষেত্রে যদি ফাইলটি করাপ্টেড থাকে বা ওই লোকেশনে ফাইলটি না পাওয়া যায় তাহলে এরর হয় কারন প্রোগ্রামটি ফাইলটিকে পড়তে পারে না। এরর হলে এক্সেপশনের মত প্রোগ্রাম ক্রাশ করে না। যখন কোন এরর হবে তখন ইউজারকে বোধগম্য মেসেজ এবং ইনস্ট্রাকশন দিয়ে প্রোগ্রামটি কন্টিনিও করানো কে এরর হ্যান্ডেলিং বলা হয়।

NSException ক্লাসের মতই NSError ক্লাসের ইনস্ট্যান্স দিয়ে ফেইল হওয়া অপারেশন তথা এরর সম্পর্কে যাবতীয় তথ্য পাওয়া যায়। NSError ক্লাসের প্রধান প্রোপার্টিগুলো নিচে দেওয়া হলঃ

  • domain (NSString) ঃ সব এরর গুলো কে কতগুলো ডোমেইনে ভাগ করা হয়েছে। এই ডোমেইন দিয়ে এরর গুলোকে সুন্দরভাবে hierarchy তে সাজানো হয়েছে।
  • code (NSInteger) ঃ code হল একটি নির্দিষ্ট ডোমেইনে এররের ইউনিক আইডি (ID)
  • userInfo (NSDictionary) ঃ NSException ক্লাসের মতই userInfo এরর সম্পর্কে অতিরিক্ত তথ্য রাখে

আগেই বলেছি যে userInfo তে তথ্যগুলো কি-ভ্যালু পেয়ার (key-value pair) হিসেবে থাকে। আবার NSException এর তুলনায় NSError এ বেশী সংখ্যক তথ্য থাকে। গুরুত্বপুর্ন কয়েকটি কি (key) হল NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, NSUnderlyingErrorKey। মজার ব্যপার হল এররের ধরন অনুযায়ী userInfo ডিকশনারীর কি(key) গুলো কমবেশী হয়। যেমন ফাইল লোড করতে গিয়ে যে এরর হয় তাতে NSFilePathErrorKey নামে একটি কি(key) থাকে যাতে ফাইলটির লোকেশন থাকে।

এরর হ্যান্ডেলিং (Handling Errors) ঃ
এরর হ্যান্ডেল করার জন্য এক্সেপশনের মত @try-@catch ব্লক বা আলাদা কিছু লিখতে হয় না। যেসব মেথড বা ফাংশনে এরর হতে পারে সেসবে একটি বাড়তি আর্গুমেন্ট হিসেবে NSError ক্লাসের অবজেক্ট পাঠানো হয়। মেথড অথবা ফাংশনটি যদি কোন কারনে ফেইল করে (কোন এরর হয়) তাহলে NO অথবা nil রিটার্ন করে এবং NSError ক্লাসের এই অবজেক্টটিতে এরর ডিটেইলস অ্যাসাইন হয়। কিন্তু যদি মেথড/ফাংশনটি সাকসেসফুলি এক্সিকিউট হয় তাহলে এই মেথডটি রিটার্ন টাইপ অনুযায়ী যে ডাটা রিটার্ন করার কথা ছিল তাই রিটার্ন করে।

অবজেক্টিভ সি এর বেশিরভাগ মেথডগুলো আর্গুমেন্ট হিসেবে NSError ক্লাসের অবজেক্টের Indirect রেফারেন্স নিয়ে থাকে। Indirect রেফারেন্স হল মুলত পয়েন্টার যা অন্য পয়েন্টারকে পয়েন্ট করে থাকে। এতে যে সুবিধা পাওয়া যায় তা হল, মেথডটি ওই আর্গুমেন্টটিকে NSError ক্লাসের সম্পুর্ন নতুন একটি ইনস্ট্যান্সে পয়েন্ট করাতে পারে। মেথডের মধ্যে ওই পয়েন্টার যে ইনস্ট্যান্সকে পয়েন্ট করে আছে তার প্রোপার্টিগুলোতে ভ্যালু অ্যাসাইন করা হয়। মেথডের এরর আর্গুমেন্টটি যদি Indirect রেফারেন্স এক্সেপ্ট করে তাহলে নিচের মত দুইবার পয়েন্টার চিহ্ন (double-pointer notation) দিয়ে ইনস্ট্যান্স পাওয়া যায়।

(NSError **)error

শুরুতেই এরর হ্যান্ডেল করার একটি সাদামাটা প্রোগ্রাম দেখা যাক। নিচের main.m ফাইলের মত করে আমাদের main.m ফাইলটি এডিট করি। এ প্রোগ্রামে শুরুতেই path নামের একটি ভ্যারিয়েবলে একটি ফাইলের পাথ দেওয়া হয়েছে। মুলত আমরা এই file.md ফাইলটির ডাটাগুলো পড়তে চাচ্ছি। এরপর NSError ক্লাসের একটি ইনস্ট্যান্স error নেওয়া হল। পরবর্তী লাইনে stringWithContentsOfFile:encoding:error: মেথডকে কিছু আর্গুমেন্ট সহ কল করা হয়েছে। এই মেথডটি অবজেক্টিভ সি এর বিল্টইন মেথড যা প্রথম আর্গুমেন্টের লোকেশন থেকে ফাইল লোড করে ফাইলের ডাটা (NSString টাইপ) রিটার্ন করে। তৃতীয় প্যারামিটারটি NSError ক্লাসের অবজেক্ট যা রেফারেন্স অপারেটর (&) দিয়ে পাঠানো হয়। &error মুলত একটি পয়েন্টার যা NSError ক্লাসের ইনস্ট্যান্স *error কে পয়েন্ট করে থাকবে।

// main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
    
        NSString *path = @"/path/to/file.md";
        
        NSError *error;
        NSString *content = [NSString stringWithContentsOfFile: path
                                                      encoding: NSUTF8StringEncoding
                                                         error: &error];
        
        if (content == nil) {
            // Method failed
            NSLog(@"Error loading file %@!", path);
            NSLog(@"Domain: %@", [error domain]);
            NSLog(@"Error Code: %ld", [error code]);
            NSLog(@"Description: %@", [error localizedDescription]);
            NSLog(@"Reason: %@", [error localizedFailureReason]);
            NSLog(@"Recovery procedure: %@",[error localizedRecoverySuggestion]);
        } else {
            // Method succeeded
            NSLog(@"Date Loaded from file: %@", content);
        }
    }
    return 0;
}

file.md ফাইলটি যদি ওই লোকেশনে না থাকে তাহলে stringWithContentsOfFile মেথডটি nil বা NO রিটার্ন করে এবং error অবজেক্টে এররের ডিটেইলস অ্যাসাইন হয়। অন্যথায় মেথডটি ফাইলের কনটেন্ট রিড করে তা রিটার্ন করে যা NSString টাইপ content ভ্যারিয়েবলে অ্যাসাইন হয়। এরপর content এর ভ্যালু চেক করে যদি NO বা nil হয় তাহলে error অবজেক্টের প্রোপার্টিগুলো code, domain, localizedDescription, localizedFailureReason ইত্যাদি এক্সেস করে এরর সম্পর্কে যাবতীয় তথ্য পাওয়া যায়।

যদি এমন হয় যে আপনি শুধু জানতে চাচ্ছেন যে এরর হয়েছে কিনা; এররের কারন না জানলেও চলবে সেক্ষেত্রে error ইনস্ট্যান্সের জন্য শুধুমাত্র NULL পাঠালেই চলবে। নিচের কোডটুকু খেয়াল করিঃ

        NSString *content = [NSString stringWithContentsOfFile:fileToLoad
                                                      encoding:NSUTF8StringEncoding
                                                         error: NULL];
        
        if (content == nil) {
           // error occured  
        } else {
            // Method succeeded
           }

বিল্ট-ইন এরর (Built-in Errors) ঃ
NSException ক্লাসের মত NSError ক্লাসের ও কিছু বিল্ট-ইন এরর ডোমেইন রয়েছে যা দিয়ে এরর গুলো কে রিপ্রেজেন্ট করা যায়। প্রধান কয়েকটি বিল্ট-ইন এরর ডোমেইন এর নাম নিচে দেওয়া হলঃ

  • NSMachErrorDomain
  • NSPOSIXErrorDomain
  • NSOSStatusErrorDomain
  • NSCocoaErrorDomain

আমাদের প্রোগ্রামগুলোতে সবচেয়ে বেশী যে এরর গুলো ঘটবে সেগুলো NSCocoaErrorDomain এর অন্তর্ভুক্ত। তারপরও চাইলে আরো লো-লেভেলের এরর ডোমেইনের এরর ইনফরমেশন পাওয়া যাবে। যেমন আমাদের উপরের main.m ফাইলে যদি নিচের লাইনটি লেখি তাহলে NSPOSIXErrorDomain এর এরর ইনফরমেশন পাওয়া যাবে।

 NSLog(@"Lower Level Domain Error : %@",error.userInfo[NSUnderlyingErrorKey]);

এই লাইনটির আউটপুট হবে
Lower Level Domain Error : Error Domain=NSPOSIXErrorDomain Code=2 “The operation couldn’t be completed. No such file or directory”

আগেই বলেছি যে সকল এরর গুলো কতগুলো ডোমেইন এ ভাগ করা হয়েছে। প্রত্যেক ডোমেইনের অন্তর্গত সকল এররের নির্দিষ্ট ও ইউনিক কোড (code) থাকে যা দিয়ে স্পেসিফিক এরর ইনফরমেশন পাওয়া যায়। Foundation Constants Reference ডকুমেন্টটিতে কিছু enum দিয়ে NSCocoaErrorDomain ডোমেইনের প্রায় সকল কোডের (code) বর্ননা দেওয়া আছে। উদাহরন স্বরুপ নিচের প্রোগ্রামে দেখা যাচ্ছে NSCocoaErrorDomain এর NSFileReadNoSuchFileError নামক কোডের এরর খোজার জন্য প্রোগ্রামটি ওত পেতে বসে আছে।

if (content == nil) {
    if ([error.domain isEqualToString:@"NSCocoaErrorDomain"] &&
        error.code == NSFileReadNoSuchFileError) {
        NSLog(@"That file doesn't exist!");
        NSLog(@"Path: %@", [[error userInfo] objectForKey:NSFilePathErrorKey]);
    } else {
        NSLog(@"Some other kind of read occurred");
    }
}

কাস্টম এরর (Custom Errors) ঃ
বড় সাইজের IOS ও OS X এর অ্যাপ্লিকেশনে বিভিন্ন মেথডে বিভিন্ন ধরনের এরর হওয়ার সম্ভাবনা থাকে যা সংখ্যায় খুব কম নয়। একারনে প্রোগ্রামারের সুবিধার্থে ও ইউজারের কাছে আরও সহজবোধ্য মেসেজ পাঠানোর উদ্দেশ্যে কাস্টম এরর বানানো হয়। বেস্ট প্র্যাকটিস হিসেবে সব এররগুলো কে একটি আলাদা হেডার ফাইলে (For Ex- RestaurantErrors.h) লেখা হয়। পরবর্তীতে প্রোগ্রামের যেসব ইমপ্লিমেনটেশন ফাইলে এই কাস্টম এরর গুলো ব্যবহার করা হবে সেখানে ইমপোর্ট করে নিলেই হবে।

//RestaurantErrors.h
NSString *RestaurantErrorDomain = @"net.Nuhil.Restaurant.ErrorDomain";

enum {
    MenuListNotLoadedError,
    MenuListEmptyError,
    RestaurantInternalError
};

কাস্টম এরর ডোমেইনের নাম যেকোন কিছুই হতে পারে, তবে CompanyName.ProjectOrFrameWorkName.ErrorDomain রাখাটাই বেস্ট প্র্যাকটিস। এরর কোড কনস্ট্যান্ট গুলো কে enum এর মধ্যে ডিফাইন করা হয়।

//  main.m
#import <Foundation/Foundation.h>
#import "RestaurantErrors.h"

NSString *getFoodTitleWithId( int foodId, NSError **error) {
   
    NSArray *Food = @[];
    
    int max = [Food count];
    
    if (max == 0) {
        if (error != NULL) {
            NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"MenuList"
                                       " Currently Empty."};
            
            *error = [NSError errorWithDomain: RestaurantErrorDomain
                                         code: MenuListEmptyError
                                     userInfo: userInfo];
        }
        return nil;
    }
    return Food[foodId];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int searchFor = 2;
        NSError *error;
        NSString *title = getFoodTitleWithId(searchFor, &error);
        
        if (title == nil) {
            // If Failed
            NSLog(@"Title could not be found");
            NSLog(@"Domain: %@", error.domain);
            NSLog(@"Error Code: %ld", error.code);
            NSLog(@"Description: %@", [error localizedDescription]);
            
        } else {
            // Succeeded
            NSLog(@"Selected Food Title Found!");
            NSLog(@"%@", title);
        }
    }
    return 0;
}

main.m এর main মেথড এ দেখা যাচ্ছে যে আরগুমেন্ট হিসেবে একটি ইনডেক্স বা আইডি (searchFor) এবং NSError ক্লাসের একটি Indirect রেফারেন্স &error সহ getFoodTitleWithId কে কল করা হযেছে। মেথডটিতে &error পাঠানোর উদ্দেশ্য হল যদি মেথডের ভেতর কোন এরর হয় তাহলে এররের প্রোপার্টিগুলোর ভ্যালু যাতে এই error ইনস্ট্যান্স এ অ্যাসাইন হয়। যদি ২য় আরগুমেন্টে NULL পাঠানো হত তাহলে এরর সম্পর্কিত কোন ইনফরমেশন আর পাওয়া যেত না। যদি এরর হত তাহলে মেথডটি শুধুমাত্র NO অথবা nil রিটার্ন করত।

getFoodTitleWithId মেথডে FoodId তে 2 এবং NSArray ক্লাসের Indirect রেফারেন্স প্যারামিটার হিসেবে এসেছে। মেথডের শুরুতেই NSArray টাইপ একটি ভ্যারিয়েবল ডিক্লেয়ার করা হয়েছে যাতে আইটেম সংখ্যা শুন্য। অর্থাত পরের লাইনে max এর ভ্যালু শুন্য হবে। এপর্যায়ে আমরা চাচ্ছি যেহেতু Food লিস্টটিতে কোন আইটেম নেই তাই একটি কাস্টম এরর থ্রো করাব। তাই যখন max এর ভ্যালু শুন্য এবং NSError এর Indirect রেফারেন্সটি NULL নাহয় তখন কাস্টম এররটির প্রোপার্টিগুলোর ভ্যালু পপুলেট করা হচ্ছে এবং অবশেষে nil রিটার্ন করে দেওয়া হয়েছে।

উপরের প্রোগ্রামটি রান করালে নিচের মত আউটপুট আসবে।

2014-06-10 00:56:19.817 restaurant[904:303] Title could not be found
2014-06-10 00:56:19.819 restaurant[904:303] Domain: net.Nuhil.Restaurant.ErrorDomain
2014-06-10 00:56:19.819 restaurant[904:303] Error Code: 1
2014-06-10 00:56:19.820 restaurant[904:303] Description: MenuList Currently Empty.
Program ended with exit code: 0

RestaurantErrorDomain এ এরর কোডগুলো যেহেতু enum টাইপ সেহেতু এরর কোডগুলোর ভ্যালুগুলো শুন্য থেকে শুরু করে ২ পর্যন্ত। তাই MenuListEmptyError এর ভ্যালু ১।

পরিশিষ্ঠঃ
এই চ্যাপ্টারের মাধ্যমেই আমাদের ব্যাসিক ওব্জেক্টিভ-সি লার্নিং সেকশন শেষ হয়ে যাচ্ছে। এর পরের সেকশনে আসছে রিয়াল লাইফ গ্রাফিক্যাল অ্যাপ্লিকেশন তৈরি সম্পর্কিত ১০টি চ্যাপ্টার। উক্ত সেকশন শেষ হবার পর আসবে ব্যাসিক সুইফ্ট ল্যাঙ্গুয়েজ লার্নিং সেকশন।

অনেকেই হয়ত ভেবে থাকতে পারেন যে, Apple যেহেতু iOS অ্যাপ ডেভেলপমেন্ট এর জন্য তাদের নতুন ল্যাঙ্গুয়েজ ঘোষণা করেছে তাহলে আর অবজেক্টিভ শিখে কি লাভ। কিন্তু এ ধারনা একদম ভুল। প্রথমত, এখন পর্যন্ত অ্যাপ স্টোরে ১০ লাখেরও বেশি অ্যাপ জীবন্ত আছে যেগুলো অব্জেক্টিভ-সি তে করা। সেই অ্যাপ গুলোর মেইন্টেইনেন্স, আপডেট, বাগ ফিক্সিং ইত্যাদির জন্য আরও সামনে ৬/৭ বছর iOS এবং অব্জেক্টিভ-সি এর ব্যবহার চলবেই। দ্বিতীয়ত, তাদের নতুন ঘোষণা মতে, একটি প্রজেক্টে অথবা অ্যাপ্লিকেশনে একি সাথে অবজেক্টিভ-সি এবং সুইফ্ট ল্যাঙ্গুয়েজ কোড থাকতে পারে এবং রান হতে পারে। অর্থাৎ তারা চাচ্ছে, আস্তে আস্তে অব্জেক্টিভ-সি এর জায়গাটা কমে আসুক, একবারে না। তৃতীয়ত, অনেক ডেভেলপার বা কোম্পানি হয়ত চাইতেই পারে যে তাদের সামনের অ্যাপ্লিকেশনগুলো তারা অবেজক্টিভ-সি দিয়েই করবে কারন সুইফ্ট -এ দক্ষ ডেভেলপার এই মুহূর্তে হয়ত তারা ব্যবস্থা করতে পারবেন না। চতুর্থত, হাজার হাজার থার্ড পার্টি লাইব্রেরী, ফ্রেমওয়ার্কও কিন্তু অব্জেক্টীভ-সি তে করা যেগুলো হয়ত আপনি আপনার নেক্সট প্রজেক্টে ব্যবহার করবেন বলে ভেবে রেখেছেন। অন্যদিকে, অনেকেই হয়ত চাইবেন তাদের পরবর্তী প্রজেক্টটি নতুন ল্যাঙ্গুয়েজ দিয়ে করতে। অর্থাৎ আপনি যদি ফ্রেশ ভাবে iOS অ্যাপ ডেভেলপমেন্ট শুরু করতে চান তাহলে শুধুমাত্র সুইফ্ট শেখা শুরু করতে পারেন কিন্তু যদি ক্যারিয়ার হিসেবে দেখতে চান তাহলে আপনাকে দুটো ল্যাঙ্গুয়েজ সম্পর্কে ধারনা রাখতেই হবে।

Laravel4 Migrations and Seeding

Standard

So far we have created the virtual host for our laravel4 application named “thisdayinbangladesh.dev” and also set up the basic laravel4 installation. Now in this lesson we will create the migrations and seeders needed for our application.

“Migrations are a type of version control for your database. They allow a team to modify the database schema and stay up to date on the current schema state. Migrations are typically paired with the Schema Builder to easily manage your application’s scheme.”

“Laravel also includes a simple way to seed your database with test data using seed classes.”

First thing first. Open up the Mac Terminal and enter inside the “thisdayinbangladesh” folder under MAMP’s “htdocs” directory. All of the following commands should be executed from inside the project folder.

Create migration files for all of our currently needed tables:
Execute these commands one by one,

php artisan migrate:make create_days_table --table=days --create
php artisan migrate:make create_facts_table --table=facts --create
php artisan migrate:make create_types_table --table=types --create
php artisan migrate:make create_fact_type_table --table=fact_type --create

These commands set up the basic migration scripts that we’ll be using to create the database tables

Update the migration files:
Edit “database/migrations/TODAYS_FULL_DATE_create_days_table.php” file and update the “up()” method to include a new column on the “days” table and to remove the “timestamp” column.

	public function up()
	{
		Schema::create('days', function(Blueprint $table)
		{
			$table->increments('id');
			$table->string('day');
		});
	}

Update the “up()” method of “database/migrations/TODAYS_FULL_DATE_create_facts_table.php” file to the following,

	public function up()
	{
		Schema::create('facts', function(Blueprint $table)
		{
			$table->increments('id');
                        $table->integer('day_id');
			$table->string('title');
                        $table->longText('description');
                        $table->timestamps();
		});
	}

Update the “up()” method of “database/migrations/TODAYS_FULL_DATE_create_types_table.php” file to the following,

	public function up()
	{
		Schema::create('types', function(Blueprint $table)
		{
			$table->increments('id');
                        $table->string('type');
		});
	}

Update the “up()” method of “database/migrations/TODAYS_FULL_DATE_create_fact_type_table.php” file to the following,

	public function up()
	{
		Schema::create('fact_type', function(Blueprint $table)
		{
			$table->increments('id');
                        $table->integer('fact_id');
                        $table->integer('type_id');
		});
	}

Prepare the Seeder files to add sample data to those tables:
Create a file within the “app/database/seeds” folder that has the same name as the table. So, for our “days” table create a file named “DayTableSeeder.php” inside “seeds” folder containing the following full code,

<?php 
class DayTableSeeder extends Seeder {
 
    public function run()
    {        
        for ($i = 1; $i <= 31; $i++) {
            for ($j = 1; $j <= 12; $j++) {
                Day::create(array(
                    'day' => sprintf("%02s", $i).'-'.sprintf("%02s", $j)
                ));             
            }
        }

    }
 
}

Here I am inserting some “day-month” patterned days rows in the “days” table using a simple loop. Please, don’t judge my PHP knowledge on this. This is not about showing optimized, minimal, smell free coding.
Also, create other seeder files for those remaining tables into which we want to push some sample data even before launching the app.
Create “FactTableSeeder.php” with following code. Here we are inserting some sample facts that holds the fact’s sample title and sample description just for checking out data retrieval with proper relations later on the next lesson.

<?php
 
class FactTableSeeder extends Seeder {
 
    public function run()
    {        
        for ($i = 1; $i <= 372; $i++) {
                Fact::create(array(
                    'day_id' => $i,
                    'title'   => 'Fact-'.$i,
                    'description' => 'Description of fact-'.$i,
                ));
        }

    }
 
}

Now create the “TypeTableSeeder.php” file with the following code,

<?php
 
class TypeTableSeeder extends Seeder {
 
    public function run()
    {        
        $types = array ('good', 'bad', 'revolutionary', 'birth', 'death');
        
        foreach ($types as $key=>$value) {
                Type::create(array(
                    'type' => $value,
                ));
        }

    }
 
}

And finally create the “FactTypeTableSeeder.php” file with,

<?php
 
class FactTypeTableSeeder extends Seeder {
 
    public function run()
    {        
        for ($i = 1; $i <= 10; $i++) {
            FactType::create(array(
                'fact_id' => $i,
                'type_id' => rand(1,5)
            ));             
        }

    }
 
}

As you can see, we are assigning some “facts” to some “types” and making rows in the “fact_type” table pairing “fact_id” with “type_id” as for our sample data.

Update the DatabaseSeeder file:
Update the “run()” method of the “DatabaseSeeder.php” file. That should already be there inside “app/database/seeds” folder with the following code,

	public function run()
	{
		Eloquent::unguard();

		$this->call('DayTableSeeder');
                $this->call('FactTableSeeder');
                $this->call('TypeTableSeeder');
                $this->call('FactTypeTableSeeder');
	}

Run Migration and Seed:
Execute the following commands one by one.

php artisan migrate
php artisan db:seed

If the commands executed successfully then you should see those tables along with an extra tables named “migrations” under your “thisdayinbangladesh” database. Also there should be our sample data in each table if the Seeding happens successfully!

Note:
As one “fact” will have multiple “types” and vise versa. You should name the Pivot table (“fact_type” in this case) of the “facts” and “types” table by taking the singular name of the alphabetically first table then the second table with an underscore in the middle.
Also there should be two columns at least, to hold the relation which should be named by taking the singular name of one table with its primary key along with an underscore in the middle. Such as “fact_id”, “type_id”.
But wait, don’t be scared. There is still flexibility while naming those tables and columns for using with Eloquent ORM. You will have to just pass those non-conventional table names or column names as parameters in corresponding methods while creating ORM relations among tables or while creating migrations tables.
We just followed the conventional naming pattern in this lesson to make thing simple and understandable.

What next?:
The next step is to make proper Routing for our application. Which is ready now!

Installing Laravel4 in Mac OSX with MAMP

Standard

Re-cap:
In the past and first tutorial of this series we learned how to create a virtual host with MAMP. In this second tutorial, we will install laravel4 PHP framework and will go another step toward developing a full laravel4 application series.

Precautions:

  • Make sure your installed PHP version is greater or equal to 5.3.7
  • Also make sure the MCrypt PHP extension is enabled
  • For pretty URL the mod_rewrite module is enabled on Apache

1. Install Composer Globally:

The following command will just check a few PHP settings and then download composer.phar

$ curl -sS https://getcomposer.org/installer | php

You can run these commands to easily access composer from anywhere on your system

$ sudo mv composer.phar /usr/local/bin/composer

2. Create a laravel project named “thisdayinbangladesh” inside our MAMP’s “htdocs” directory:

Going inside the “htdocs” directory

$ cd /Applications/MAMP/htdocs

Creating the project there

$ composer create-project laravel/laravel thisdayinbangladesh --prefer-dist

3. A small permission issue:

Lets enter into our project directory

$ cd thisdayinbangladesh/

Change the permission of the storage folder to writable

$ sudo chmod -R 0777 app/storage/

4. Basic Configuration:

Update line number 29 of the “app/config/app.php” file to

$ 'url' => 'http://thisdayinbangladesh.dev/',

and line 42 to

$ 'timezone' => 'Asia/Dhaka',

5. Database configuration:
Though configuring database is not necessary to have a first run but lets complete this step as we will not do any editing on the config files later.


'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'thisdayinbangladesh',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
 ),

6. Lets have a test run:

Open the URL of your laravel application as we created earlier using virtual host; on the browser. That is http://thisdayinbangladesh.dev/
You should arrive!

How to Set Up Virtual Host with MAMP on a Mac OSX

Standard

1. Update the hosts file:
We are going to add an example domain named “thisdayinbangladesh.dev” as a virtual host through this tutorial. Lets first add this in the “hosts” file by typing the following command on the Terminal,

sudo nano /etc/hosts

Then find the following line,

127.0.0.1 localhost

update this to following,

127.0.0.1 localhost thisdayinbangladesh.dev

Also to add our domain to the IPv6 version find the following line somewhere bottom of the file,

fe80::1%lo0 localhost

update this line to the following one,

fe80::1%lo0 localhost thisdayinbangladesh.dev

Finally press “ctrl+x” and then “y” to save and exit this file.

2. Update httpd.conf file
Open up this file by typing the following command,

sudo nano /Applications/MAMP/conf/apache/httpd.conf

find out the following line,

#Include /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf

It might have a “#” symbol at the very beginning of the line. Delete the symbol to activate this statement. If the line is not anywhere of the “httpd.conf” file then add the above line anywhere of this file. Now, save and exit this file.

3. Update httpd-vhosts.conf file
Now go into the “/Applications/MAMP/conf/apache/” folder by executing,

cd /Applications/MAMP/conf/apache/

If you can see a folder named “extra” then enter inside it otherwise create this “extra” folder and then enter inside it.
Now update (If there was already a “extra” folder then there should be a “httpd-vhosts.conf” file other wise just create the “httpd-vhosts.conf” file inside “extra” folder) the “httpd-vhosts.conf” file by typing the following,

sudo nano httpd-vhosts.conf

Then the final “httpd-vhosts.conf” file should have the following code block. Whether you add these lines or updated the existing file.

NameVirtualHost *:80

<VirtualHost 0.0.0.0:80>
DocumentRoot "/Applications/MAMP/htdocs"
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "/Applications/MAMP/htdocs/thisdayinbangladesh/public"
    ServerName thisdayinbangladesh.dev
    <Directory "/Applications/MAMP/htdocs/thisdayinbangladesh/public">
        Options All
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

Here “thisdayinbangladesh” is a project directory; actually a laravel4 project directory inside my “htdocs” folder and we added the “public” folder thats resides in “thisdayinbangladesh” folder as the document root cause laravel4 needs to point to the “public” folder as the application root URL.
In this stage of this example. You can forget about laravel4 and simply create the directory “/thisdayinbangladesh/public” inside your “htdocs” folder and place a simple “index.html” file there inside the “public” folder.

4. Restart:
Simply stop and re-start the Apache service from your MAMP control panel. Then try out the URL on your browser. http://thisdayinbangladesh.dev You should see your “index.html” file!

Important:
This tutorial will take you through out a nice series of posts on which a full laravel4 web application has been developed. That service is already LIVE HERE. So, stay tuned!

Update:
The next tutorial is ready Installing Laravel4 in Mac OSX with MAMP. So, you are another step close toward the full laravel4 application development series.

A jQuery Plugin Snippet for Sublime Text 2

Standard

Sublime_Text_Logo
You can simply (yes really simply) create a code snippet on Sublime Text 2 for faster code completion by clicking on the Tools -> New Snippet  menu of sublime and by editing the snippet template according to your choice.
You will have to edit/update line number 3 to the code block you want to have while calling the snippet by a shortcode. Uncomment line 6 & define a shortcode typing which you want to generate that code block.  Also uncomment line 8 and define whether you want that snippet to be workable only in a specific scope of scripting.

Finally save the file in /Users/USERNAME/Library/Application\ Support/Sublime\ Text\ 2/Packages/User  (Mac OSX) folder with a name along with .sublime-snippet extension.

Screen Shot 2013-08-29 at 4.26.04 PM

Here I have made a simple snippet which contains a template of a basic JQuery plugin. You can Download this snippet file and paste it inside the folder /Users/USERNAME/Library/Application\ Support/Sublime\ Text\ 2/Packages/User (Mac OSX)

Screen Shot 2013-08-29 at 4.12.36 PMThen you can start writing jquery on any type of file (As I did not define any scope) and the snippet keyword should appear.

Screen Shot 2013-08-29 at 4.14.14 PM

Press tab key by typing jqueryplugin OR enter/return key by selecting that suggestion and it will generate a basic template of a jquery plugin there for you.

Screen Shot 2013-08-29 at 4.14.22 PM

Deploy Application in Amazon Beanstalk inside free EC2 Instance with RDS

Standard

aws-beanstalk
Amazon has virtual cloud server (instance) and many other web services. They offers a 1 year free micro tier to be used by anyone who own an international currency Debit/Credit card like Payoneer debit master card. There are several services included with the free tier that can be used along with the EC2 instance without any extra charge. Beanstalk deployment framework is one of the free services you are eligible for.

For no additional charge, AWS Elastic Beanstalk allows you quickly and easily deploy your Java, PHP, Python, Ruby, and .NET applications using many of the services offered in the free usage tier.” -Amazon. Beanstalk is similar to other deployment frameworks like appfog, pagodabox etc. Moreover if you use Beanstalk then you will still have access to your virtual cloud server in which the deployment will be running. “Behind the scenes, Elastic Beanstalk handles the provisioning of a load balancer and Amazon EC2 resources.” -Amazon. Thats why I prefere Beanstalk than others who are in my knowledge.

In this post we will cover the steps of creating an account for Amazon EC2 free tier, configuring local machine for Amazon CLI tools, creating a sample app locally, creating an elastic beanstalk php environment, deploying a sample php app, creating an Amazon RDS and connecting to it via PHP and mySQL workbench, git version controlling of the deployed app, setting up a custom classified domain to our newly deployed app environment. Tired to read these ? I am sure you will feel best after having this journey 🙂

1. Create an account for Amazon web service from here . You can see the service list that will be included with your free tier for your further using. Note that, the sign up process is simple and you will be asked to provide your card detail on the second step and finally you will have to verify your phone number. You will have to put your phone number then to click “call now” button then a 4 digit pin number will be appeared on your browser. After that an automated call will reach your phone. You should either speak the pin number slowly or input the pin number in your mobile keypad if it supports touchtone functionality. I always spoke the pin after listening the bot girl’s voice 🙂 Continue reading