Generic answer: if password-based authentication can be performed offline, then the app necessarily contains all that is needed to decide whether a given password is the correct one or not. This unavoidably implies that by dumping the complete app code and contents, the attacker can "emulate" the offline server on his own machine, and try passwords at his leisure, limited only by the computing power he can muster (how many PC he will buy or rent) and the inherent computational cost of verifying every single password. This is called an offline dictionary attack situation.
At best, in such a case, you can raise the cost by using slow hashing (many iterations of an underlying hash function, or something similar) and salts (to prevent parallel attacks, cost sharing, precomputed tables...). See this answer for a complete treatment.
Offline dictionary attacks are a worry. Being in such a situation is not comfortable. If users accept a 1-second wait for authentication on their smartphone, then the smartphone CPU must necessarily be able to verify a password within one second. An attacker with a few good PC will then be able to "try" at least a few dozen passwords per second; make that a few hundreds if the hashing algorithm maps well to what off-the-shelf GPU can do, a few thousands is the attacker is industrious and went straight to some FPGA implementation. Also, the attacker is often patient, and can afford to wait one or two weeks before obtaining a user password. This adds up to, very roughly, one billion password tries for the attacker.
Training average users to choose passwords which will resist a brute force attack of one billion potential passwords, is hard. See this question for some discussion on the subject. On the other hand, if you get to choose the password (not the user), then select a sequence of 15 random letters: that's 70 bits of entropy, and this will resist long enough to deter attackers (a password space of size 270 is huge).
Note that salts are not meant to be secret, and are effective even when known by the attacker. A secret salt is not a salt, but a key. You cannot have that in your situation: one must assume that the attacker dumped and reverse-engineered the complete app code. To a large extent, your context is similar to that of password-based encryption: protecting the confidentiality of a piece of data, with regards to a secret password, without specific hardware.